Decompiled source of TimbnLivingTown v1.0.0

Timbn.GraveyardKeeperTwo.LivingTown/Timbn.GraveyardKeeperTwo.LivingTown.dll

Decompiled 7 hours 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.Configuration;
using LazyBearTechnology;
using Microsoft.CodeAnalysis;
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("Timbn.GraveyardKeeperTwo.LivingTown")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyDescription("Gives the village and the port a daily life. Villagers follow routines, visit the graveyard, hold funerals for the bodies you bury and sometimes ask you for a small job.")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0+3f81e6d85137cd53fa8c38ef21c0539f46474a59")]
[assembly: AssemblyProduct("Timbn Living Town")]
[assembly: AssemblyTitle("Timbn.GraveyardKeeperTwo.LivingTown")]
[assembly: AssemblyVersion("1.0.0.0")]
[module: RefSafetyRules(11)]
[CompilerGenerated]
internal sealed class <>z__ReadOnlyList<T> : IEnumerable, ICollection, IList, IEnumerable<T>, IReadOnlyCollection<T>, IReadOnlyList<T>, ICollection<T>, IList<T>
{
	int ICollection.Count => _items.Count;

	bool ICollection.IsSynchronized => false;

	object ICollection.SyncRoot => this;

	object IList.this[int index]
	{
		get
		{
			return _items[index];
		}
		set
		{
			throw new NotSupportedException();
		}
	}

	bool IList.IsFixedSize => true;

	bool IList.IsReadOnly => true;

	int IReadOnlyCollection<T>.Count => _items.Count;

	T IReadOnlyList<T>.this[int index] => _items[index];

	int ICollection<T>.Count => _items.Count;

	bool ICollection<T>.IsReadOnly => true;

	T IList<T>.this[int index]
	{
		get
		{
			return _items[index];
		}
		set
		{
			throw new NotSupportedException();
		}
	}

	public <>z__ReadOnlyList(List<T> items)
	{
		_items = items;
	}

	IEnumerator IEnumerable.GetEnumerator()
	{
		return ((IEnumerable)_items).GetEnumerator();
	}

	void ICollection.CopyTo(Array array, int index)
	{
		((ICollection)_items).CopyTo(array, index);
	}

	int IList.Add(object value)
	{
		throw new NotSupportedException();
	}

	void IList.Clear()
	{
		throw new NotSupportedException();
	}

	bool IList.Contains(object value)
	{
		return ((IList)_items).Contains(value);
	}

	int IList.IndexOf(object value)
	{
		return ((IList)_items).IndexOf(value);
	}

	void IList.Insert(int index, object value)
	{
		throw new NotSupportedException();
	}

	void IList.Remove(object value)
	{
		throw new NotSupportedException();
	}

	void IList.RemoveAt(int index)
	{
		throw new NotSupportedException();
	}

	IEnumerator<T> IEnumerable<T>.GetEnumerator()
	{
		return ((IEnumerable<T>)_items).GetEnumerator();
	}

	void ICollection<T>.Add(T item)
	{
		throw new NotSupportedException();
	}

	void ICollection<T>.Clear()
	{
		throw new NotSupportedException();
	}

	bool ICollection<T>.Contains(T item)
	{
		return _items.Contains(item);
	}

	void ICollection<T>.CopyTo(T[] array, int arrayIndex)
	{
		_items.CopyTo(array, arrayIndex);
	}

	bool ICollection<T>.Remove(T item)
	{
		throw new NotSupportedException();
	}

	int IList<T>.IndexOf(T item)
	{
		return _items.IndexOf(item);
	}

	void IList<T>.Insert(int index, T item)
	{
		throw new NotSupportedException();
	}

	void IList<T>.RemoveAt(int index)
	{
		throw new NotSupportedException();
	}
}
[CompilerGenerated]
internal sealed class <>z__ReadOnlySingleElementList<T> : IEnumerable, ICollection, IList, IEnumerable<T>, IReadOnlyCollection<T>, IReadOnlyList<T>, ICollection<T>, IList<T>
{
	private sealed class Enumerator : IDisposable, IEnumerator, IEnumerator<T>
	{
		object IEnumerator.Current => _item;

		T IEnumerator<T>.Current => _item;

		public Enumerator(T item)
		{
			_item = item;
		}

		bool IEnumerator.MoveNext()
		{
			if (!_moveNextCalled)
			{
				return _moveNextCalled = true;
			}
			return false;
		}

		void IEnumerator.Reset()
		{
			_moveNextCalled = false;
		}

		void IDisposable.Dispose()
		{
		}
	}

	int ICollection.Count => 1;

	bool ICollection.IsSynchronized => false;

	object ICollection.SyncRoot => this;

	object IList.this[int index]
	{
		get
		{
			if (index != 0)
			{
				throw new IndexOutOfRangeException();
			}
			return _item;
		}
		set
		{
			throw new NotSupportedException();
		}
	}

	bool IList.IsFixedSize => true;

	bool IList.IsReadOnly => true;

	int IReadOnlyCollection<T>.Count => 1;

	T IReadOnlyList<T>.this[int index]
	{
		get
		{
			if (index != 0)
			{
				throw new IndexOutOfRangeException();
			}
			return _item;
		}
	}

	int ICollection<T>.Count => 1;

	bool ICollection<T>.IsReadOnly => true;

	T IList<T>.this[int index]
	{
		get
		{
			if (index != 0)
			{
				throw new IndexOutOfRangeException();
			}
			return _item;
		}
		set
		{
			throw new NotSupportedException();
		}
	}

	public <>z__ReadOnlySingleElementList(T item)
	{
		_item = item;
	}

	IEnumerator IEnumerable.GetEnumerator()
	{
		return new Enumerator(_item);
	}

	void ICollection.CopyTo(Array array, int index)
	{
		array.SetValue(_item, index);
	}

	int IList.Add(object value)
	{
		throw new NotSupportedException();
	}

	void IList.Clear()
	{
		throw new NotSupportedException();
	}

	bool IList.Contains(object value)
	{
		return EqualityComparer<T>.Default.Equals(_item, (T)value);
	}

	int IList.IndexOf(object value)
	{
		if (!EqualityComparer<T>.Default.Equals(_item, (T)value))
		{
			return -1;
		}
		return 0;
	}

	void IList.Insert(int index, object value)
	{
		throw new NotSupportedException();
	}

	void IList.Remove(object value)
	{
		throw new NotSupportedException();
	}

	void IList.RemoveAt(int index)
	{
		throw new NotSupportedException();
	}

	IEnumerator<T> IEnumerable<T>.GetEnumerator()
	{
		return new Enumerator(_item);
	}

	void ICollection<T>.Add(T item)
	{
		throw new NotSupportedException();
	}

	void ICollection<T>.Clear()
	{
		throw new NotSupportedException();
	}

	bool ICollection<T>.Contains(T item)
	{
		return EqualityComparer<T>.Default.Equals(_item, item);
	}

	void ICollection<T>.CopyTo(T[] array, int arrayIndex)
	{
		array[arrayIndex] = _item;
	}

	bool ICollection<T>.Remove(T item)
	{
		throw new NotSupportedException();
	}

	int IList<T>.IndexOf(T item)
	{
		if (!EqualityComparer<T>.Default.Equals(_item, item))
		{
			return -1;
		}
		return 0;
	}

	void IList<T>.Insert(int index, T item)
	{
		throw new NotSupportedException();
	}

	void IList<T>.RemoveAt(int index)
	{
		throw new NotSupportedException();
	}
}
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.LivingTown
{
	internal static class Areas
	{
		public const string VillageKey = "timbn_living_town_where_village";

		public const string TownKey = "timbn_living_town_where_town";

		private static readonly string[] _towns = new string[4] { "town_A1_day_all", "town_A2_day_all", "town_A3_day_all", "town_A4_day_all" };

		public static List<string> FreeInVillage()
		{
			return FreeIn("villagers_day_all");
		}

		public static List<List<string>> FreeInTowns()
		{
			return (from people in _towns.Select(FreeIn)
				where people.Count > 0
				select people).ToList();
		}

		private static List<string> FreeIn(string group)
		{
			return (from npc in TimbnNpcs.GroupMemberIds(@group).Distinct()
				where !TimbnQuests.HasQuestRun(npc)
				select npc).ToList();
		}
	}
	internal sealed class Deliveries
	{
		private sealed class Parcel
		{
			public string Kind { get; }

			public string[] Items { get; }

			public Parcel(string kind, string[] items)
			{
				Kind = kind;
				Items = items;
			}
		}

		private const int _daysPerDelivery = 7;

		private const int _daysToDeliver = 7;

		private const string _lapsedKey = "timbn_living_town_errand_lapsed";

		private static readonly Parcel[] _parcels = new Parcel[3]
		{
			new Parcel("keepsake", new string[3] { "trinkets_wood", "trinkets_stone", "trinkets_bronze" }),
			new Parcel("vegetables", new string[1] { "box_vegetables" }),
			new Parcel("potion", new string[1] { "heal_potion" })
		};

		private readonly Plugin _plugin;

		private readonly Random _random = new Random();

		private TimbnQuest _errand;

		private bool _turningOff;

		public Deliveries(Plugin plugin)
		{
			_plugin = plugin;
		}

		public void Start()
		{
			_errand = ((TimbnFrameworkPlugin)_plugin).Quests.Add("timbn_living_town_delivery", (Action<TimbnQuestBuilder>)delegate(TimbnQuestBuilder quest)
			{
				quest.Repeatable().ExpiresAfter(7).Setup((Func<TimbnQuestRun, bool>)Pick)
					.Giver((Func<TimbnQuestRun, string>)((TimbnQuestRun run) => run.NpcId("sender")))
					.Icon("portrait_icon_hero")
					.Marker((Func<bool>)(() => ((TimbnFrameworkPlugin)_plugin).Settings.IsEnabled(PluginConfig.OfferMarkers)), (Func<bool>)(() => ((TimbnFrameworkPlugin)_plugin).Settings.IsEnabled(PluginConfig.HandInMarkers)))
					.OfferWhen((Func<bool>)(() => ((TimbnFrameworkPlugin)_plugin).Settings.IsEnabled(PluginConfig.VillagerDeliveries)))
					.Description()
					.Offer((Action<TimbnQuestOffer>)delegate(TimbnQuestOffer talk)
					{
						Parcel[] parcels = _parcels;
						foreach (Parcel parcel in parcels)
						{
							((TimbnTalk<TimbnQuestOffer>)(object)talk).When((Func<bool>)(() => IsParcel(parcel)), (Action<TimbnLines>)delegate(TimbnLines then)
							{
								((TimbnTalk<TimbnLines>)(object)then).Line("ask_" + parcel.Kind, Array.Empty<object>());
							}, (Action<TimbnLines>)null);
						}
						talk.Accept((Action<TimbnLines>)delegate(TimbnLines then)
						{
							((TimbnTalk<TimbnLines>)(object)((TimbnTalk<TimbnLines>)(object)then).RunCode((Action)HandOver)).Line("thanks", Array.Empty<object>());
						}, (Action<TimbnNeeds>)null, (Action<TimbnCost>)null).Decline((Action<TimbnLines>)null);
					})
					.TalksWith((Func<TimbnQuestRun, string>)((TimbnQuestRun run) => run.NpcId("recipient")))
					.Cost((Action<TimbnCost, TimbnQuestRun>)delegate(TimbnCost cost, TimbnQuestRun run)
					{
						cost.Item(run.Item("parcel"), 1);
					})
					.Reward((Action<TimbnRewards, TimbnQuestRun>)delegate(TimbnRewards reward, TimbnQuestRun run)
					{
						reward.Money(TimbnItems.SalePrice(run.Item("parcel")));
					})
					.Talk((Action<TimbnQuestTalk>)delegate(TimbnQuestTalk talk)
					{
						talk.Accept((Action<TimbnLines>)delegate(TimbnLines then)
						{
							Parcel[] parcels = _parcels;
							foreach (Parcel parcel in parcels)
							{
								((TimbnTalk<TimbnLines>)(object)then).When((Func<bool>)(() => IsParcel(parcel)), (Action<TimbnLines>)delegate(TimbnLines thanks)
								{
									((TimbnTalk<TimbnLines>)(object)thanks).Line("thanks_" + parcel.Kind, Array.Empty<object>());
								}, (Action<TimbnLines>)null);
							}
						}).Decline((Action<TimbnLines>)null);
					})
					.OnCancel((Action<TimbnQuestRun>)PayForParcel);
			});
			((TimbnFrameworkPlugin)_plugin).Events.NewDayStarted((Action<int>)delegate
			{
				if (_random.Next(7) == 0)
				{
					_errand.NewRun();
				}
			});
			((TimbnFrameworkPlugin)_plugin).Settings.Changed<bool>(PluginConfig.VillagerDeliveries, (Action<bool>)delegate(bool on)
			{
				if (!on)
				{
					_turningOff = true;
					_errand.Cancel();
					_turningOff = false;
				}
			});
		}

		private bool Pick(TimbnQuestRun run)
		{
			List<string> list = Areas.FreeInVillage();
			List<List<string>> list2 = Areas.FreeInTowns();
			List<string> list3 = (from text in _parcels.SelectMany((Parcel parcel) => parcel.Items)
				where TimbnItems.SalePrice(text) > 0
				select text).ToList();
			if (list.Count == 0 || list2.Count == 0 || list3.Count == 0)
			{
				TimbnFrameworkPlugin<Plugin>.Logger.LogInfo((object)$"Deliveries: no errand today, {list.Count} free villagers, {list2.Count} open town districts and {list3.Count} parcels.");
				return false;
			}
			List<string> list4 = list2[_random.Next(list2.Count)];
			bool flag = _random.Next(2) == 0;
			List<string> list5 = (flag ? list : list4);
			List<string> list6 = (flag ? list4 : list);
			string item = list3[_random.Next(list3.Count)];
			run.Npc("sender", list5[_random.Next(list5.Count)]).Npc("recipient", list6[_random.Next(list6.Count)]).Text("where", flag ? "timbn_living_town_where_town" : "timbn_living_town_where_village")
				.Item("parcel", item)
				.Text("delivering", DeliveringKey(_parcels.First((Parcel parcel) => parcel.Items.Contains(item))));
			return true;
		}

		private static string DeliveringKey(Parcel parcel)
		{
			return "timbn_living_town_delivery_delivering_" + parcel.Kind;
		}

		private bool IsParcel(Parcel parcel)
		{
			TimbnQuestRun currentRun = _errand.CurrentRun;
			if (currentRun != null)
			{
				return parcel.Items.Contains(currentRun.Item("parcel"));
			}
			return false;
		}

		private void HandOver()
		{
			//IL_001b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0025: Expected O, but got Unknown
			TimbnQuestRun currentRun = _errand.CurrentRun;
			if (currentRun != null)
			{
				TimbnItems.GiveToPlayer(new Item(currentRun.Item("parcel"), 1));
			}
		}

		private void PayForParcel(TimbnQuestRun? run)
		{
			//IL_0016: Unknown result type (might be due to invalid IL or missing references)
			//IL_001c: Invalid comparison between Unknown and I4
			if (!_turningOff && (int)_errand.Status.GetValueOrDefault() == 2 && run != null)
			{
				int num = TimbnPlayer.TakeMoney(TimbnItems.SalePrice(run.Item("parcel")));
				TimbnFrameworkPlugin<Plugin>.Logger.LogInfo((object)string.Format("Deliveries: {0} was never delivered, the player paid {1} for it.", run.Item("parcel"), num));
				TimbnUI.Notify("timbn_living_town_errand_lapsed");
			}
		}
	}
	internal sealed class Requests
	{
		private const int _daysPerRequest = 7;

		private const int _daysToCook = 7;

		private readonly Plugin _plugin;

		private readonly Random _random = new Random();

		public Requests(Plugin plugin)
		{
			_plugin = plugin;
		}

		public void Start()
		{
			TimbnQuest craving = ((TimbnFrameworkPlugin)_plugin).Quests.Add("timbn_living_town_request", (Action<TimbnQuestBuilder>)delegate(TimbnQuestBuilder quest)
			{
				quest.Repeatable().ExpiresAfter(7).Setup((Func<TimbnQuestRun, bool>)Pick)
					.Giver((Func<TimbnQuestRun, string>)((TimbnQuestRun run) => run.NpcId("villager")))
					.Icon("portrait_icon_hero")
					.Marker((Func<bool>)(() => ((TimbnFrameworkPlugin)_plugin).Settings.IsEnabled(PluginConfig.OfferMarkers)), (Func<bool>)(() => ((TimbnFrameworkPlugin)_plugin).Settings.IsEnabled(PluginConfig.HandInMarkers)))
					.OfferWhen((Func<bool>)(() => ((TimbnFrameworkPlugin)_plugin).Settings.IsEnabled(PluginConfig.VillagerRequests)))
					.Description()
					.Offer((Action<TimbnQuestOffer>)delegate(TimbnQuestOffer talk)
					{
						((TimbnTalk<TimbnQuestOffer>)(object)talk).Line("ask", Array.Empty<object>()).Accept((Action<TimbnLines>)delegate(TimbnLines then)
						{
							((TimbnTalk<TimbnLines>)(object)then).Line("thanks", Array.Empty<object>());
						}, (Action<TimbnNeeds>)null, (Action<TimbnCost>)null).Decline((Action<TimbnLines>)null);
					})
					.Cost((Action<TimbnCost, TimbnQuestRun>)delegate(TimbnCost cost, TimbnQuestRun run)
					{
						cost.Item(run.Item("dish"), 1);
					})
					.Reward((Action<TimbnRewards, TimbnQuestRun>)delegate(TimbnRewards reward, TimbnQuestRun run)
					{
						reward.Money(TimbnItems.SalePrice(run.Item("dish")));
					})
					.Talk((Action<TimbnQuestTalk>)delegate(TimbnQuestTalk talk)
					{
						((TimbnTalk<TimbnQuestTalk>)(object)talk).Line("ask", Array.Empty<object>()).Accept((Action<TimbnLines>)delegate(TimbnLines then)
						{
							((TimbnTalk<TimbnLines>)(object)then).Line("thanks", Array.Empty<object>());
						}).Decline((Action<TimbnLines>)null);
					});
			});
			((TimbnFrameworkPlugin)_plugin).Events.NewDayStarted((Action<int>)delegate
			{
				if (_random.Next(7) == 0)
				{
					craving.NewRun();
				}
			});
			((TimbnFrameworkPlugin)_plugin).Settings.Changed<bool>(PluginConfig.VillagerRequests, (Action<bool>)delegate(bool on)
			{
				if (!on)
				{
					craving.Cancel();
				}
			});
		}

		private bool Pick(TimbnQuestRun run)
		{
			List<string> list = Areas.FreeInVillage();
			List<List<string>> list2 = Areas.FreeInTowns();
			List<string> list3 = KnownDishes();
			List<(List<string>, string)> list4 = list2.Select((List<string> people) => (people: people, TownKey: "timbn_living_town_where_town")).ToList();
			if (list.Count > 0)
			{
				list4.Add((list, "timbn_living_town_where_village"));
			}
			if (list4.Count == 0 || list3.Count == 0)
			{
				TimbnFrameworkPlugin<Plugin>.Logger.LogInfo((object)$"Requests: no request today, {list.Count} free villagers, {list2.Count} open town districts and {list3.Count} known dishes.");
				return false;
			}
			var (list5, text) = list4[_random.Next(list4.Count)];
			run.Npc("villager", list5[_random.Next(list5.Count)]).Text("where", text).Item("dish", list3[_random.Next(list3.Count)]);
			return true;
		}

		private static List<string> KnownDishes()
		{
			GameBalance me = GameBalance.Me;
			HashSet<string> hashSet = new HashSet<string>();
			CraftDef[] array = TimbnPlayer.KnownCrafts(new string[4] { "kitchen_oven", "kitchen_oven_t2", "kitchen_table", "kitchen_table_t2" });
			foreach (CraftDef val in array)
			{
				if (((CraftDefBase)val).outputItems.chanceOutputItems.Count != 1 || ((CraftDefBase)val).outputItems.groupChanceOutputItems.Count != 0)
				{
					continue;
				}
				ChanceOutputItem val2 = ((CraftDefBase)val).outputItems.chanceOutputItems[0];
				if (!val2.isStarGroup && TimbnItems.SalePrice(val2.id) != 0)
				{
					ItemDef dataOrNull = ((GameBalanceBase)me).GetDataOrNull<ItemDef>(val2.id);
					if (dataOrNull != null && dataOrNull.gameResOnUse.Exists((ExpressionGameRes effect) => effect.name == "energy"))
					{
						hashSet.Add(val2.id);
					}
				}
			}
			return hashSet.ToList();
		}
	}
	internal sealed class Funeral
	{
		private sealed class Mourner
		{
			public TimbnNpc Npc { get; }

			public string? StartPost { get; set; }

			public GDPointData? Seat { get; set; }

			public string? ReturnPost { get; set; }

			public bool InChurch { get; set; }

			public bool Speaker { get; set; }

			public bool Reached { get; set; }

			public int Place { get; set; } = -1;

			public Mourner(TimbnNpc npc)
			{
				Npc = npc;
			}
		}

		internal const float LastArrival = 0.375f;

		internal const int MinimumMourners = 5;

		private const float _checkedAt = 1f / 12f;

		private const float _beginsAt = 0.5f;

		private const float _leavesBy = 0.625f;

		private const string _altar = "base_god_church_player";

		private const string _choir = "crossroad_chor_player_01";

		private const string _aisle = "base_ceremony_nun_church_02";

		private const string _parishionerTag = "parishioner_point";

		private const int _firstSpeechMinutes = 60;

		private const int _laterSpeechMinutes = 30;

		private const int _viewMinutes = 5;

		private const float _lineGap = 0.8f;

		private const float _seatTaken = 0.1f;

		private static readonly string[] _startPosts = new string[3] { "base_god_church_player", "church_parishioner_1", "church_parishioner_2" };

		private static readonly string[] _returnPosts = new string[3] { "church_parishioner_3", "church_parishioner_1", "church_parishioner_2" };

		private readonly Plugin _plugin;

		private readonly GraveyardVisitors _visitors;

		private readonly TimbnSaveData<FuneralData> _data;

		private readonly List<Mourner> _mourners = new List<Mourner>();

		private readonly List<Mourner> _toSpeak = new List<Mourner>();

		private readonly List<Mourner> _line = new List<Mourner>();

		private readonly HashSet<string> _usedPosts = new HashSet<string>();

		private readonly List<Vector3> _usedSeats = new List<Vector3>();

		private readonly Random _random = new Random();

		private HashSet<string> _graves = new HashSet<string>();

		private WgoData? _grave;

		private Mourner? _speaker;

		private Vector3 _stand;

		private int _spoken;

		private bool _leaving;

		public bool IsOn => _grave != null;

		public Func<bool> MoreComing { get; set; } = () => false;

		public static int Capacity => ChurchStop.Seats().Count() + _startPosts.Length;

		private static double BeginsAt => ((double)TimbnClock.Day + 0.5) * 24.0 * 60.0;

		private static bool Overdue => TimbnClock.Minutes >= ((double)TimbnClock.Day + 0.625) * 24.0 * 60.0;

		private bool CeremonyDue
		{
			get
			{
				if (!(TimbnClock.Minutes >= BeginsAt))
				{
					if (_mourners.Count > 0 && !MoreComing())
					{
						return _mourners.All((Mourner mourner) => mourner.InChurch);
					}
					return false;
				}
				return true;
			}
		}

		private GraveyardPaths Paths => _visitors.Paths;

		public Funeral(Plugin plugin, GraveyardVisitors visitors)
		{
			_plugin = plugin;
			_visitors = visitors;
			_data = ((TimbnFrameworkPlugin)plugin).Saves.Register<FuneralData>((Action<FuneralData>)null);
			visitors.Paths.Avoid = Crowd;
		}

		public void Check()
		{
			//IL_01b5: Unknown result type (might be due to invalid IL or missing references)
			//IL_01bb: Invalid comparison between Unknown and I4
			//IL_0267: Unknown result type (might be due to invalid IL or missing references)
			FuneralData data = ((TimbnData<FuneralData>)(object)_data).Current;
			if (data.CheckedDay == TimbnClock.Day || TimbnClock.TimeOfDay < 1f / 12f || !((TimbnFrameworkPlugin)_plugin).Settings.IsEnabled(PluginConfig.Funerals))
			{
				return;
			}
			data.CheckedDay = TimbnClock.Day;
			Clear();
			Dictionary<string, WgoData> dictionary = (from grave in TimbnWorld.GravesWithBodies()
				group grave by grave.UniqueId.Id).ToDictionary((IGrouping<string, WgoData> group) => group.Key, (IGrouping<string, WgoData> group) => group.First());
			HashSet<string> hashSet;
			if (!data.Seeded)
			{
				data.Seeded = true;
				FuneralData funeralData = data;
				hashSet = new HashSet<string>();
				foreach (string key in dictionary.Keys)
				{
					hashSet.Add(key);
				}
				funeralData.KnownGraves = hashSet;
				GraveyardVisitors.Log($"funeral, {dictionary.Count} grave(s) already filled in, so none of them gets one.");
				return;
			}
			data.KnownGraves.IntersectWith(dictionary.Keys);
			data.TriedGraves.IntersectWith(dictionary.Keys);
			List<KeyValuePair<string, WgoData>> list = dictionary.Where((KeyValuePair<string, WgoData> pair) => !data.KnownGraves.Contains(pair.Key)).ToList();
			if (list.Count == 0)
			{
				GraveyardVisitors.Log("funeral, no new grave was filled in, so none today.");
				return;
			}
			if ((int)TimbnClock.Weekday.GetValueOrDefault() == 5)
			{
				GraveyardVisitors.Log("funeral, a new grave was filled in but it is the sermon day, so tomorrow.");
				return;
			}
			if (TimbnClock.TimeOfDay >= 0.375f)
			{
				GraveyardVisitors.Log("funeral, a new grave was found too late for anyone to come today, so tomorrow.");
				return;
			}
			_grave = list[0].Value;
			hashSet = new HashSet<string>();
			foreach (string item in list.Select((KeyValuePair<string, WgoData> pair) => pair.Key))
			{
				hashSet.Add(item);
			}
			_graves = hashSet;
			GraveyardVisitors.Log("funeral, a new grave at " + GraveyardVisitors.Flat(_grave.Position) + " gets one today.");
			if (_graves.All(data.TriedGraves.Contains))
			{
				GraveyardVisitors.Log("funeral, it is the second try for this grave, so there is no third.");
				Held();
			}
			else
			{
				data.TriedGraves.UnionWith(_graves);
			}
		}

		public TimbnNpcSteps Welcome(TimbnNpc npc, Func<TimbnNpc, TimbnNpcSteps> wayOut)
		{
			//IL_002b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0030: Unknown result type (might be due to invalid IL or missing references)
			//IL_0048: Unknown result type (might be due to invalid IL or missing references)
			//IL_004d: Unknown result type (might be due to invalid IL or missing references)
			//IL_010e: Unknown result type (might be due to invalid IL or missing references)
			//IL_01cd: Unknown result type (might be due to invalid IL or missing references)
			//IL_01d2: Unknown result type (might be due to invalid IL or missing references)
			//IL_01d8: Expected O, but got Unknown
			//IL_01d8: Unknown result type (might be due to invalid IL or missing references)
			//IL_01f5: Unknown result type (might be due to invalid IL or missing references)
			//IL_0201: Unknown result type (might be due to invalid IL or missing references)
			//IL_0212: Unknown result type (might be due to invalid IL or missing references)
			Vector3? val = ChurchStop.Landing("tp_RT_church_enter");
			if (val.HasValue)
			{
				Vector3 valueOrDefault = val.GetValueOrDefault();
				val = ChurchStop.Landing("tp_RT_church_exit");
				if (val.HasValue)
				{
					Vector3 valueOrDefault2 = val.GetValueOrDefault();
					Mourner mourner = new Mourner(npc);
					int count = _mourners.Count;
					_mourners.Add(mourner);
					if (count < _startPosts.Length)
					{
						mourner.StartPost = _startPosts[count];
						mourner.ReturnPost = _returnPosts[count];
						mourner.Speaker = true;
						_toSpeak.Add(mourner);
					}
					else
					{
						GDPointData val2 = FreeSeat();
						if (val2 != null)
						{
							mourner.Seat = val2;
							_usedSeats.Add(val2.Position);
						}
						else
						{
							mourner.StartPost = FreePost();
						}
					}
					string text = ((mourner.Seat != null) ? "stands at a pew seat point" : ((mourner.StartPost == null) ? "has nowhere to stand" : ("stands at " + mourner.StartPost)));
					GraveyardVisitors.Log("funeral, " + npc.Id + " goes to the church and " + text + ((count < _startPosts.Length) ? $", speaker {count + 1}" : "") + ".");
					TimbnNpcSteps val3 = Paths.Walk(npc, new TimbnNpcSteps(), valueOrDefault).Teleport(valueOrDefault2);
					GDPointData seat = mourner.Seat;
					if (seat != null)
					{
						val3.WalkNavmesh(seat.Position.x, seat.Position.z).Face(seat.Direction);
					}
					else if (mourner.StartPost != null)
					{
						GDPointData val4 = TimbnWorld.Point(mourner.StartPost);
						if (val4 != null)
						{
							val3.WalkNavmesh(val4.Id).Face((Direction)2);
						}
					}
					return val3.RunCode((Action<TimbnNpc>)delegate
					{
						mourner.InChurch = true;
					}).Decide((Func<TimbnNpc, TimbnNpcSteps>)((TimbnNpc _) => Act(mourner, wayOut)));
				}
			}
			GraveyardVisitors.Log("funeral, the church door can't be found, so " + npc.Id + " goes home.");
			return wayOut(npc);
		}

		public void Forget(TimbnNpc npc)
		{
			Mourner mourner = _mourners.FirstOrDefault((Mourner mourner2) => mourner2.Npc == npc);
			if (mourner != null)
			{
				_mourners.Remove(mourner);
				_toSpeak.Remove(mourner);
				_line.Remove(mourner);
				if (_speaker == mourner)
				{
					_speaker = null;
				}
				TryFinish();
			}
		}

		public void Clear()
		{
			_grave = null;
			_graves = new HashSet<string>();
			_mourners.Clear();
			_toSpeak.Clear();
			_line.Clear();
			_usedPosts.Clear();
			_usedSeats.Clear();
			_speaker = null;
			_spoken = 0;
			_leaving = false;
		}

		private IReadOnlyList<Vector2> Crowd(TimbnNpc walker)
		{
			if (_grave != null && !_line.Any((Mourner mourner) => mourner.Npc == walker))
			{
				List<Vector2> list = new List<Vector2>();
				list.AddRange(_line.Where((Mourner mourner) => mourner.Place >= 0).Select((Func<Mourner, Vector2>)((Mourner mourner) => new Vector2(mourner.Npc.Position.x, mourner.Npc.Position.z))));
				return new <>z__ReadOnlyList<Vector2>(list);
			}
			return Array.Empty<Vector2>();
		}

		private GDPointData? FreeSeat()
		{
			List<GDPointData> list = (from seat in ChurchStop.Seats()
				where !_usedSeats.Any((Vector3 taken) => TimbnWorld.FlatDistance(taken, seat.Position) < 0.1f)
				select seat).ToList();
			if (list.Count != 0)
			{
				return list[_random.Next(list.Count)];
			}
			return null;
		}

		private string? FreePost()
		{
			HashSet<string> reserved = _startPosts.Concat(_returnPosts).ToHashSet();
			List<GDPointData> list = (from point in TimbnWorld.PointsWithTag("parishioner_point")
				where point.Enabled && !reserved.Contains(point.Id) && !_usedPosts.Contains(point.Id)
				select point).ToList();
			if (list.Count == 0)
			{
				return null;
			}
			string id = list[_random.Next(list.Count)].Id;
			_usedPosts.Add(id);
			return id;
		}

		private TimbnNpcSteps? Act(Mourner mourner, Func<TimbnNpc, TimbnNpcSteps> wayOut)
		{
			//IL_0115: Unknown result type (might be due to invalid IL or missing references)
			if (_grave == null)
			{
				return wayOut(mourner.Npc);
			}
			if (!_leaving && _speaker == null)
			{
				if (_toSpeak.Count > 0 && Overdue)
				{
					GraveyardVisitors.Log($"funeral, {_toSpeak.Count} speaker(s) never spoke and it is late, so everyone leaves without them.");
					_toSpeak.Clear();
					BeginLeaving();
				}
				else if (_toSpeak.Count == 0 && CeremonyDue)
				{
					BeginLeaving();
				}
				else if (_toSpeak.Count > 0 && _toSpeak[0] == mourner && (_spoken > 0 || CeremonyDue))
				{
					return Speak(mourner, wayOut);
				}
			}
			if (_leaving)
			{
				return Leave(mourner, wayOut);
			}
			return new TimbnNpcSteps().WaitGameMinutes(1f).Decide((Func<TimbnNpc, TimbnNpcSteps>)((TimbnNpc _) => Act(mourner, wayOut)));
		}

		private void Held()
		{
			((TimbnData<FuneralData>)(object)_data).Current.KnownGraves.UnionWith(_graves);
		}

		private TimbnNpcSteps Speak(Mourner mourner, Func<TimbnNpc, TimbnNpcSteps> wayOut)
		{
			//IL_0061: Unknown result type (might be due to invalid IL or missing references)
			//IL_0067: Expected O, but got Unknown
			//IL_008e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0099: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fe: Unknown result type (might be due to invalid IL or missing references)
			Held();
			_speaker = mourner;
			int num = ((_spoken == 0) ? 60 : 30);
			GraveyardVisitors.Log($"funeral, {mourner.Npc.Id} speaks at the choir for {num} minutes.");
			TimbnNpcSteps val = new TimbnNpcSteps();
			if (mourner.StartPost != "base_god_church_player")
			{
				GDPointData val2 = TimbnWorld.Point("base_god_church_player");
				if (val2 != null)
				{
					val.WalkStraight(val2.Position.x, val2.Position.z);
				}
			}
			val.Walk("crossroad_chor_player_01").Face((Direction)4).WaitGameMinutes((float)num)
				.Walk("base_god_church_player");
			if (mourner.ReturnPost != null)
			{
				GDPointData val3 = TimbnWorld.Point(mourner.ReturnPost);
				if (val3 != null)
				{
					val.WalkStraight(val3.Position.x, val3.Position.z).Face((Direction)2);
				}
			}
			return val.RunCode((Action<TimbnNpc>)delegate
			{
				Spoke(mourner);
			}).Decide((Func<TimbnNpc, TimbnNpcSteps>)((TimbnNpc _) => Act(mourner, wayOut)));
		}

		private void Spoke(Mourner mourner)
		{
			_toSpeak.Remove(mourner);
			if (_speaker == mourner)
			{
				_speaker = null;
			}
			_spoken++;
		}

		private void BeginLeaving()
		{
			//IL_0077: Unknown result type (might be due to invalid IL or missing references)
			//IL_007c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0081: Unknown result type (might be due to invalid IL or missing references)
			//IL_0086: Unknown result type (might be due to invalid IL or missing references)
			Held();
			_leaving = true;
			_line.AddRange(_mourners.Where((Mourner mourner) => mourner.InChurch));
			WgoData grave = _grave;
			if (grave != null && !_visitors.GraveStop.TryFindStandSpot(grave, Paths, out _stand))
			{
				GraveyardVisitors.Log("funeral, no ground to stand on by the grave, so they stand just below it.");
				_stand = grave.Position + Vector3.back;
			}
			GraveyardVisitors.Log($"funeral, the speaking is over and {_line.Count} leave the church for the grave.");
		}

		private TimbnNpcSteps? Leave(Mourner mourner, Func<TimbnNpc, TimbnNpcSteps> wayOut)
		{
			//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_0073: Unknown result type (might be due to invalid IL or missing references)
			//IL_0078: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ec: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f2: Expected O, but got Unknown
			//IL_00cb: Unknown result type (might be due to invalid IL or missing references)
			//IL_018e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0194: Unknown result type (might be due to invalid IL or missing references)
			//IL_019f: Unknown result type (might be due to invalid IL or missing references)
			if (!_line.Contains(mourner))
			{
				_line.Add(mourner);
			}
			Vector3? val = ChurchStop.Landing("tp_RT_church_enter");
			if (val.HasValue)
			{
				Vector3 valueOrDefault = val.GetValueOrDefault();
				val = ChurchStop.Landing("tp_RT_church_exit");
				if (val.HasValue)
				{
					Vector3 valueOrDefault2 = val.GetValueOrDefault();
					if (!mourner.Speaker && _mourners.Any((Mourner other) => other.Speaker && !other.Reached))
					{
						return new TimbnNpcSteps().WaitGameMinutes(1f).Decide((Func<TimbnNpc, TimbnNpcSteps>)((TimbnNpc _) => Leave(mourner, wayOut)));
					}
					TimbnNpcSteps val2 = new TimbnNpcSteps();
					int num = (mourner.Speaker ? _line.IndexOf(mourner) : _line.Take(_line.IndexOf(mourner)).Count((Mourner other) => !other.Speaker));
					if (num > 0)
					{
						val2.WaitGameMinutes((float)num);
					}
					if (mourner.Speaker)
					{
						val2.WalkNavmesh("base_ceremony_nun_church_02").RunCode((Action<TimbnNpc>)delegate
						{
							mourner.Reached = true;
						});
					}
					return val2.WalkNavmesh(valueOrDefault2.x, valueOrDefault2.z).Teleport(valueOrDefault).Decide((Func<TimbnNpc, TimbnNpcSteps>)((TimbnNpc _) => GoToPlace(mourner, wayOut)));
				}
			}
			return wayOut(mourner.Npc);
		}

		private TimbnNpcSteps? GoToPlace(Mourner mourner, Func<TimbnNpc, TimbnNpcSteps> wayOut)
		{
			//IL_006b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0077: Unknown result type (might be due to invalid IL or missing references)
			//IL_0081: Expected O, but got Unknown
			int place = _line.IndexOf(mourner);
			if (_grave == null || place < 0)
			{
				return wayOut(mourner.Npc);
			}
			return Paths.Walk(mourner.Npc, new TimbnNpcSteps(), SpotAt(place)).RunCode((Action<TimbnNpc>)delegate
			{
				mourner.Place = place;
			}).Decide((Func<TimbnNpc, TimbnNpcSteps>)((TimbnNpc _) => Turn(mourner, wayOut)));
		}

		private TimbnNpcSteps? Turn(Mourner mourner, Func<TimbnNpc, TimbnNpcSteps> wayOut)
		{
			//IL_005c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0062: Expected O, but got Unknown
			//IL_0133: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f1: Unknown result type (might be due to invalid IL or missing references)
			//IL_0097: Unknown result type (might be due to invalid IL or missing references)
			int place = _line.IndexOf(mourner);
			WgoData grave = _grave;
			if (grave == null || place < 0)
			{
				return wayOut(mourner.Npc);
			}
			TimbnNpcSteps val = new TimbnNpcSteps();
			if (place > 0)
			{
				if (place < mourner.Place)
				{
					Paths.Walk(mourner.Npc, val, SpotAt(place)).RunCode((Action<TimbnNpc>)delegate
					{
						mourner.Place = place;
					});
				}
				return val.WaitGameMinutes(1f).Decide((Func<TimbnNpc, TimbnNpcSteps>)((TimbnNpc _) => Turn(mourner, wayOut)));
			}
			if (mourner.Place != 0)
			{
				Paths.Walk(mourner.Npc, val, SpotAt(0));
			}
			GraveyardVisitors.Log("funeral, " + mourner.Npc.Id + " looks at the grave.");
			return val.RunCode((Action<TimbnNpc>)delegate
			{
				mourner.Place = 0;
			}).Face(grave.Position).WaitGameMinutes(5f)
				.RunCode((Action<TimbnNpc>)delegate
				{
					Viewed(mourner);
				})
				.Decide(wayOut);
		}

		private void Viewed(Mourner mourner)
		{
			_line.Remove(mourner);
			TryFinish();
		}

		private Vector3 SpotAt(int place)
		{
			//IL_000e: 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_0029: 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_003f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0044: Unknown result type (might be due to invalid IL or missing references)
			//IL_004d: Unknown result type (might be due to invalid IL or missing references)
			if (place != 0)
			{
				WgoData grave = _grave;
				if (grave != null)
				{
					Vector3 to = (Vector3)(((??)ChurchStop.Landing("tp_RT_church_enter")) ?? _stand);
					return Paths.PointAlong(grave.Position, to, (float)place * 0.8f);
				}
			}
			return _stand;
		}

		private void TryFinish()
		{
			if (_grave != null && _leaving && _line.Count <= 0 && !_mourners.Any((Mourner mourner) => !mourner.InChurch))
			{
				GraveyardVisitors.Log("funeral, everyone has looked at the grave, so it is over.");
				Clear();
			}
		}
	}
	internal sealed class FuneralData
	{
		public bool Seeded { get; set; }

		public int CheckedDay { get; set; } = -1;

		public HashSet<string> KnownGraves { get; set; } = new HashSet<string>();

		public HashSet<string> TriedGraves { get; set; } = new HashSet<string>();
	}
	[BepInPlugin("Timbn.GraveyardKeeperTwo.LivingTown", "Timbn Living Town", "1.0.0")]
	[BepInDependency("Timbn.GraveyardKeeperTwo.Core", "2.0.0")]
	public class Plugin : TimbnFrameworkPlugin<Plugin>
	{
		private const string _hermClosedKey = "timbn_living_town_herm_closed";

		private const string _petKey = "timbn_living_town_pet";

		protected override void BindConfig(ConfigFile config)
		{
			PluginConfig.Bind(config);
		}

		protected override void OnAwake()
		{
			//IL_016c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0176: Unknown result type (might be due to invalid IL or missing references)
			//IL_0180: Expected O, but got Unknown
			HermDay herm = new HermDay();
			LindaDay linda = new LindaDay(this);
			LarryDay larry = new LarryDay();
			AstrologerDay astrologer = new AstrologerDay();
			ChurchVisits visits = new ChurchVisits(this);
			((TimbnFrameworkPlugin)this).Settings.While(PluginConfig.DailyRoutine, (Func<IDisposable>)(() => (IDisposable)((TimbnFrameworkPlugin)this).Npcs.Schedule("npc_herm", herm.Build())));
			((TimbnFrameworkPlugin)this).Settings.While(PluginConfig.DailyRoutine, (Func<IDisposable>)(() => (IDisposable)((TimbnFrameworkPlugin)this).Npcs.Schedule("npc_tavern_owner", linda.Build())));
			((TimbnFrameworkPlugin)this).Events.Update((Action)linda.Tick);
			((TimbnFrameworkPlugin)this).Events.SaveClosed((Action)linda.Reset);
			((TimbnFrameworkPlugin)this).Settings.Toggle(PluginConfig.DailyRoutine, (Action)delegate
			{
			}, (Action)linda.Reset);
			((TimbnFrameworkPlugin)this).Settings.While(PluginConfig.DailyRoutine, (Func<IDisposable>)(() => (IDisposable)((TimbnFrameworkPlugin)this).Npcs.Schedule("npc_larry", larry.Build())));
			((TimbnFrameworkPlugin)this).Settings.While(PluginConfig.DailyRoutine, (Func<IDisposable>)(() => (IDisposable)((TimbnFrameworkPlugin)this).Npcs.Schedule("npc_astrologer", astrologer.Build())));
			((TimbnFrameworkPlugin)this).Events.SaveClosed((Action)herm.Reset);
			((TimbnFrameworkPlugin)this).UI.AddObjectAction(new TimbnObjectAction("common_box_08", new Vector3(-104.36f, 0f, -338.2f), "timbn_living_town_pet"));
			((TimbnFrameworkPlugin)this).Events.SaveReady((Action)visits.Prepare);
			((TimbnFrameworkPlugin)this).Events.Update((Action)visits.Tick);
			((TimbnFrameworkPlugin)this).Events.SaveClosed((Action)visits.Reset);
			((TimbnFrameworkPlugin)this).Events.BeforeSave((Action)visits.SendAway);
			((TimbnFrameworkPlugin)this).Settings.Toggle(PluginConfig.ChurchVisits, (Action)delegate
			{
			}, (Action)visits.Reset);
			((TimbnFrameworkPlugin)this).Settings.Toggle(PluginConfig.Funerals, (Action)delegate
			{
			}, (Action)visits.Reset);
			((TimbnFrameworkPlugin)this).Settings.Changed<int>(PluginConfig.GratitudePerVisitor, (Action<int>)delegate
			{
				visits.Replan();
			});
			((TimbnFrameworkPlugin)this).Settings.Changed<int>(PluginConfig.MinimumChurchVisitors, (Action<int>)delegate
			{
				visits.Replan();
			});
			((TimbnFrameworkPlugin)this).Settings.Changed<int>(PluginConfig.MaximumChurchVisitors, (Action<int>)delegate
			{
				visits.Replan();
			});
			((TimbnFrameworkPlugin)this).Settings.While(PluginConfig.OpeningHours, (Func<IDisposable>)(() => ((TimbnFrameworkPlugin)this).Npcs.CloseTradeWhen("npc_herm", (Func<bool>)VendorHours.IsClosed, "timbn_living_town_herm_closed")));
			new Requests(this).Start();
			new Deliveries(this).Start();
		}
	}
	internal static class PluginConfig
	{
		public static ConfigEntry<bool> DailyRoutine { get; private set; }

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

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

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

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

		public static ConfigEntry<int> GratitudePerVisitor { get; private set; }

		public static ConfigEntry<int> MinimumChurchVisitors { get; private set; }

		public static ConfigEntry<int> MaximumChurchVisitors { get; private set; }

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

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

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

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

		public static void Bind(ConfigFile config)
		{
			DailyRoutine = config.Bind<bool>("People", "DailyRoutine", true, "Enable to have Herm, Linda, Larry and the astrologer go about their day. Turning it off puts them back in their usual spots.");
			OpeningHours = config.Bind<bool>("People", "OpeningHours", true, "Enable the vendors to be closed to trading outside of opening hours.");
			ChurchVisits = config.Bind<bool>("Crowds", "ChurchVisits", true, "Enable to have villagers and townsfolk visit the graveyard each day with crowd sizes scaling up with town gratitude.");
			Funerals = config.Bind<bool>("Crowds", "Funerals", true, "Enable villagers to hold funerals for bodies you recently buried. They gather in the church, three of them speak at the choir in turn, then everyone lines up at the grave to pay their respects. Funerals are postponed on sermon day.");
			GratitudePerVisitor = TimbnConfig.BindRange<int>(config, "Crowds", "GratitudePerVisitor", 7, "Ratio of town gratitude to additional graveyard visitors. For example, every 7 gratitude points result in one extra visitor.", 1, 50);
			MinimumChurchVisitors = TimbnConfig.BindRange<int>(config, "Crowds", "MinimumChurchVisitors", 2, "The minimum number of villagers that visit the graveyard in a day, regardless of town gratitude.", 0, 30);
			MaximumChurchVisitors = TimbnConfig.BindRange<int>(config, "Crowds", "MaximumChurchVisitors", 30, "The maximum number of villagers that visit the graveyard in a day, regardless of town gratitude.", 0, 50);
			VillagerRequests = config.Bind<bool>("Requests", "VillagerRequests", true, "Enable to have villagers in the village or town ask you to cook a dish you know the recipe for, and pay its sale price when you bring it.");
			VillagerDeliveries = config.Bind<bool>("Requests", "VillagerDeliveries", true, "Enable to have villagers in the village or town give you something to take between the village and town, and the friend pays its sale price. Deliveries expire after one week.");
			OfferMarkers = config.Bind<bool>("Requests", "OfferMarkers", false, "Shows a marker on the villager who wants to ask you for a dish or an errand, pointing the way while they're off screen.");
			HandInMarkers = config.Bind<bool>("Requests", "HandInMarkers", true, "Shows a marker on the villager waiting for your dish or parcel once you have it, pointing the way while they're off screen.");
			WriteDebugLog = TimbnConfig.BindHidden<bool>(config, "Debug", "WriteDebugLog", false, "Writes every church visit, walk and schedule decision to the log at the debug level.");
		}
	}
	internal sealed class AstrologerDay
	{
		private const string _home = "port_astrologer_spawn";

		private const string _book = "astrologer_necronomicon_03";

		private const string _pieces = "astrologer_pieces_astrologer_spot";

		private const string _meet = "27_port_astrologer_meet";

		private const float _goChance = 0.75f;

		public TimbnNpcDay Build()
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			return new TimbnNpcDay().Block("day", 0f, (Action<TimbnNpcBlock>)delegate(TimbnNpcBlock block)
			{
				block.Origin("port_astrologer_spawn").LateIntro((Func<TimbnNpcSteps, TimbnNpcSteps>)((TimbnNpcSteps steps) => steps.FireEvent("tp_to_tower"))).Loop((Func<TimbnNpcSteps, TimbnNpcSteps>)((TimbnNpcSteps steps) => steps.WaitGameMinutes(20f, 40f).Decide((Func<TimbnNpc, TimbnNpcSteps>)Pick)));
			}).OnlyWhile((Func<bool>)(() => (int)TimbnQuests.StatusOf("27_port_astrologer_meet").GetValueOrDefault() == 3)).StartWhenAt("port_astrologer_spawn")
				.ResumeWhenAt("port_astrologer_spawn")
				.LogSteps((Func<bool>)(() => PluginConfig.WriteDebugLog.Value));
		}

		private static TimbnNpcSteps? Pick(TimbnNpc npc)
		{
			//IL_0083: Unknown result type (might be due to invalid IL or missing references)
			double num = default(double);
			Random random = npc.RollChance(0.75f, ref num, "something");
			if (random == null)
			{
				return null;
			}
			switch (random.Next(3))
			{
			case 0:
				Log($"Rolled {num:0.00}, the necronomicon.");
				return Visit("astrologer_necronomicon_03");
			case 1:
				Log($"Rolled {num:0.00}, the pieces.");
				return Visit("astrologer_pieces_astrologer_spot");
			default:
				Log(string.Format("Rolled {0:0.00}, {1}.", num, "custom_02"));
				return new TimbnNpcSteps().Animate("custom_02");
			}
		}

		private static TimbnNpcSteps Visit(string spot)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			return new TimbnNpcSteps().Walk(spot).Face((Direction)2).WaitGameMinutes(15f, 30f)
				.Walk("port_astrologer_spawn")
				.Face((Direction)4);
		}

		private static void Log(string line)
		{
			if (PluginConfig.WriteDebugLog.Value)
			{
				TimbnFrameworkPlugin<Plugin>.Logger.LogDebug((object)$"day {TimbnClock.Day} {TimbnClock.TimeText} Astrologer: {line}");
			}
		}
	}
	internal sealed class HermDay
	{
		private const string _stand = "RT_herm_spawn";

		private const string _ramp = "npc_sim_village_right_side_road_34";

		private const string _road03 = "npc_sim_village_right_side_road_03";

		private const string _pond = "npc_sim_village_right_side_03";

		private const string _leave = "npc_sim_village_right_side_road_36";

		private const string _bench = "npc_sim_village_right_side_01";

		private const string _botSide = "npc_sim_village_bot_side_01";

		private const string _guard = "npc_sim_village_guard_01";

		private const string _toiletsInside = "npc_sim_village_toilets_01_inside";

		private const string _toiletsOutside = "npc_sim_village_toilets_01_outside";

		private const float _goChance = 0.75f;

		private const float _rareChance = 0.25f;

		private const float _commonChance = 0.7f;

		private const float _chainChance = 0.4f;

		private const double _quietTailMinutes = 120.0;

		private static readonly string[] _benches = new string[2] { "npc_sim_village_right_side_05", "npc_sim_village_right_side_04" };

		private static readonly Dictionary<string, Direction> _loopFacing = new Dictionary<string, Direction>
		{
			["npc_sim_village_right_side_road_39"] = (Direction)3,
			["npc_sim_village_right_side_road_02"] = (Direction)3,
			["npc_sim_village_right_side_road_03"] = (Direction)3,
			["npc_sim_village_right_side_road_35"] = (Direction)1
		};

		private static readonly string[] _common = new string[2] { "spot", "pond" };

		private static readonly string[] _uncommon = new string[2] { "bench01", "bot01" };

		private int _rareDoneDay = -1;

		private string _lastRoute = "";

		public TimbnNpcDay Build()
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			return new TimbnNpcDay().Block("working", 0.25f, (Action<TimbnNpcBlock>)delegate(TimbnNpcBlock block)
			{
				block.Origin("RT_herm_spawn").Intro((Func<TimbnNpcSteps, TimbnNpcSteps>)((TimbnNpcSteps steps) => steps.FireEvent("go_to_spawn"))).LateIntro((Func<TimbnNpcSteps, TimbnNpcSteps>)((TimbnNpcSteps steps) => steps.FireEvent("tp_to_spawn")))
					.Loop((Func<TimbnNpcSteps, TimbnNpcSteps>)((TimbnNpcSteps steps) => steps.WaitGameMinutes(10f, 30f).ChainTrips((Func<TimbnNpc, TimbnNpcTrip, TimbnNpcTrip>)((TimbnNpc herm, TimbnNpcTrip? previous) => (!TimbnWorld.IsRaining) ? Roll(herm, previous) : RollRaining(previous)))));
			}).Block("evening", 0.8f, (Action<TimbnNpcBlock>)delegate(TimbnNpcBlock block)
			{
				block.Origin("RT_herm_spawn").Intro((Func<TimbnNpcSteps, TimbnNpcSteps>)((TimbnNpcSteps steps) => steps.WalkNavmesh("npc_sim_village_right_side_road_34").GoToFreePoint("villagers_day_all", "npc_sim_village_tavern_", false))).LateIntro((Func<TimbnNpcSteps, TimbnNpcSteps>)((TimbnNpcSteps steps) => steps.GoToFreePoint("villagers_day_all", "npc_sim_village_tavern_", true)));
			}).Block("night", 11f / 12f, (Action<TimbnNpcBlock>)delegate(TimbnNpcBlock block)
			{
				block.Origin("RT_herm_spawn").Intro((Func<TimbnNpcSteps, TimbnNpcSteps>)((TimbnNpcSteps steps) => steps.Walk("npc_sim_village_tavern_road_02").Walk("npc_sim_village_right_side_road_34").WalkNavmesh("RT_herm_spawn")
					.FireEvent("tp_to_spawn")
					.FireEvent("go_home"))).LateIntro((Func<TimbnNpcSteps, TimbnNpcSteps>)((TimbnNpcSteps steps) => steps.FireEvent("tp_to_spot")));
			})
				.LogSteps((Func<bool>)(() => PluginConfig.WriteDebugLog.Value));
		}

		public void Reset()
		{
			_rareDoneDay = -1;
			_lastRoute = "";
		}

		private TimbnNpcTrip? Roll(TimbnNpc herm, TimbnNpcTrip? previous)
		{
			double minutesLeftInBlock = herm.MinutesLeftInBlock;
			if (minutesLeftInBlock < 120.0)
			{
				Log((previous == null) ? $"Staying at the stall, the next block starts in {minutesLeftInBlock:0} minutes." : $"Heading back to the stall, the next block starts in {minutesLeftInBlock:0} minutes.");
				return null;
			}
			int day = TimbnClock.Day;
			bool flag = previous != null;
			string text = ((_rareDoneDay != day) ? TodaysRare() : null);
			string text2 = ((text != null && text != _lastRoute && TimbnRolls.ThisMinute("npc_herm/rare").NextDouble() < 0.25) ? text : null);
			double num = default(double);
			Random random = herm.RollChance((text2 != null) ? 1f : (flag ? 0.4f : 0.75f), ref num, flag ? "another trip" : "a trip");
			if (random == null)
			{
				return null;
			}
			bool flag2 = random.NextDouble() < 0.699999988079071;
			List<string> list = new List<string>();
			if (text2 != null)
			{
				list.Add(text2);
			}
			list.AddRange((flag2 ? _common : _uncommon).OrderBy((string _) => random.Next()));
			list.AddRange((flag2 ? _uncommon : _common).OrderBy((string _) => random.Next()));
			if (flag)
			{
				list.Remove(_lastRoute);
			}
			List<string> list2 = new List<string>();
			foreach (string item in list)
			{
				string text3 = Destination(item, herm, random);
				string text4 = herm.BlockedReason(text3);
				if (text4 != null)
				{
					list2.Add(item + " (" + text3 + ": " + text4 + ")");
					continue;
				}
				herm.Claim(text3);
				_lastRoute = item;
				Log(((item == text2) ? ("Rolled the " + item + " route (once a day).") : $"Rolled {num:0.00}, the {item} route.") + (flag ? " Going straight on from the last trip." : "") + ((list2.Count > 0) ? (" Passed over " + string.Join(", ", list2) + ".") : ""));
				if (item == text2)
				{
					_rareDoneDay = day;
				}
				return Trip(item, text3);
			}
			Log(flag ? ("Heading back to the stall, every other route had its spot taken: " + string.Join(", ", list2) + ".") : ("Skipped, every route had its spot taken: " + string.Join(", ", list2) + ". Staying at the stall."));
			return null;
		}

		private static TimbnNpcTrip? RollRaining(TimbnNpcTrip? previous)
		{
			Log((previous == null) ? "Staying at the stall, it's raining." : "Heading back to the stall, it's started raining.");
			return null;
		}

		private TimbnNpcTrip Trip(string route, string destination)
		{
			//IL_005f: Unknown result type (might be due to invalid IL or missing references)
			return (TimbnNpcTrip)(route switch
			{
				"spot" => new TimbnNpcTrip().Leave((Func<TimbnNpcSteps, TimbnNpcSteps>)((TimbnNpcSteps steps) => steps.WalkNavmesh("npc_sim_village_right_side_road_34"))).Body((Func<TimbnNpcSteps, TimbnNpcSteps>)((TimbnNpcSteps steps) => steps.Walk(destination).Face(_loopFacing[destination]).WaitGameMinutes(15f, 30f))).Return((Func<TimbnNpcSteps, TimbnNpcSteps>)((TimbnNpcSteps steps) => steps.Walk("npc_sim_village_right_side_road_03").WalkNavmesh("npc_sim_village_right_side_road_34").WalkNavmesh("RT_herm_spawn"))), 
				"pond" => Away(viaLeave: false, (TimbnNpcSteps trip) => trip.Walk("npc_sim_village_right_side_03").WaitGameMinutes(45f)), 
				"bench01" => Away(viaLeave: true, (TimbnNpcSteps trip) => trip.GoToFreePoint((IReadOnlyList<string>)new <>z__ReadOnlySingleElementList<string>("npc_sim_village_right_side_01"), false).WaitGameMinutes(30f)), 
				"bot01" => Away(viaLeave: true, (TimbnNpcSteps trip) => trip.Walk("npc_sim_village_bot_side_01").WaitGameMinutes(30f)), 
				"guard" => Away(viaLeave: true, (TimbnNpcSteps trip) => trip.Walk("npc_sim_village_guard_01").WaitGameMinutes(20f)), 
				_ => Away(viaLeave: true, (TimbnNpcSteps trip) => trip.Walk("npc_sim_village_toilets_01_inside").WaitGameMinutes(10f).Walk("npc_sim_village_toilets_01_outside")
					.Teleport("npc_sim_village_toilets_01_outside")
					.GoToFreePoint((IReadOnlyList<string>)_benches, false)
					.WaitGameMinutes(30f)
					.Walk("npc_sim_village_right_side_road_29")
					.Walk("npc_sim_village_right_side_road_32")), 
			});
		}

		private static TimbnNpcTrip Away(bool viaLeave, Func<TimbnNpcSteps, TimbnNpcSteps> body)
		{
			//IL_000d: Unknown result type (might be due to invalid IL or missing references)
			return new TimbnNpcTrip().Leave((Func<TimbnNpcSteps, TimbnNpcSteps>)((TimbnNpcSteps steps) => (!viaLeave) ? steps.WalkNavmesh("npc_sim_village_right_side_road_34") : steps.WalkNavmesh("npc_sim_village_right_side_road_34").Walk("npc_sim_village_right_side_road_36"))).Body(body).Return((Func<TimbnNpcSteps, TimbnNpcSteps>)((TimbnNpcSteps steps) => steps.Walk("npc_sim_village_right_side_road_34").WalkNavmesh("RT_herm_spawn")));
		}

		private static void Log(string line)
		{
			if (PluginConfig.WriteDebugLog.Value)
			{
				TimbnFrameworkPlugin<Plugin>.Logger.LogDebug((object)$"day {TimbnClock.Day} {TimbnClock.TimeText} Herm: {line}");
			}
		}

		private static string Destination(string route, TimbnNpc herm, Random random)
		{
			return route switch
			{
				"pond" => "npc_sim_village_right_side_03", 
				"bench01" => "npc_sim_village_right_side_01", 
				"bot01" => "npc_sim_village_bot_side_01", 
				"guard" => "npc_sim_village_guard_01", 
				"bathroom" => "npc_sim_village_toilets_01_inside", 
				_ => ((IEnumerable<string>)_loopFacing.Keys.OrderBy((string _) => random.Next())).FirstOrDefault((Func<string, bool>)herm.IsFree) ?? _loopFacing.Keys.First(), 
			};
		}

		private static string TodaysRare()
		{
			if (TimbnRolls.ThisDay("npc_herm").Next(2) != 0)
			{
				return "bathroom";
			}
			return "guard";
		}
	}
	internal sealed class LarryDay
	{
		private const string _home = "RT_larry_spawn";

		private const string _fairy = "base_fairy_larry_05";

		private const float _goChance = 0.75f;

		public TimbnNpcDay Build()
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			return new TimbnNpcDay().Block("day", 0f, (Action<TimbnNpcBlock>)delegate(TimbnNpcBlock block)
			{
				block.Origin("RT_larry_spawn").LateIntro((Func<TimbnNpcSteps, TimbnNpcSteps>)((TimbnNpcSteps steps) => steps.FireEvent("tp_to_spawn_without_puff"))).Loop((Func<TimbnNpcSteps, TimbnNpcSteps>)((TimbnNpcSteps steps) => steps.WaitGameMinutes(20f, 40f).Decide((Func<TimbnNpc, TimbnNpcSteps>)Pick)));
			}).StartWhenAt("RT_larry_spawn").ResumeWhenAt("RT_larry_spawn")
				.LogSteps((Func<bool>)(() => PluginConfig.WriteDebugLog.Value));
		}

		private static TimbnNpcSteps? Pick(TimbnNpc npc)
		{
			//IL_003b: Unknown result type (might be due to invalid IL or missing references)
			double num = default(double);
			Random random = npc.RollChance(0.75f, ref num, "something");
			if (random == null)
			{
				return null;
			}
			if (random.Next(2) == 0)
			{
				Log(string.Format("Rolled {0:0.00}, {1}.", num, "custom_01"));
				return new TimbnNpcSteps().Animate("custom_01");
			}
			Log($"Rolled {num:0.00}, the fairy trip.");
			return Fairy();
		}

		private static TimbnNpcSteps Fairy()
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			return new TimbnNpcSteps().Walk("base_fairy_larry_05").WaitGameMinutes(15f, 30f).Walk("RT_larry_spawn")
				.FireEvent("tp_to_spawn_without_puff");
		}

		private static void Log(string line)
		{
			if (PluginConfig.WriteDebugLog.Value)
			{
				TimbnFrameworkPlugin<Plugin>.Logger.LogDebug((object)$"day {TimbnClock.Day} {TimbnClock.TimeText} Larry: {line}");
			}
		}
	}
	internal sealed class LindaDay
	{
		private const string _home = "RT_tavern_owner_spawn";

		private const string _entrance = "port_linda_start";

		private const string _doorway = "village_letter_owner_spawn";

		private const string _barFront = "port_linda_player_spot";

		private const string _safeHarbor = "29_port_linda_meet";

		private const string _concert = "68_port_concert_start";

		private const string _music = "violin";

		private const float _catX = -104.35f;

		private const float _catZ = -338.61f;

		private const float _goChance = 0.75f;

		private const float _morning = 1f / 3f;

		private const float _afternoon = 0.625f;

		private const float _night = 0.75f;

		private const float _late = 0f;

		private const float _bed = 1f / 12f;

		private const double _quietTailMinutes = 90.0;

		private readonly Plugin _plugin;

		private TimbnNpc? _performer;

		private IDisposable? _song;

		public LindaDay(Plugin plugin)
		{
			_plugin = plugin;
		}

		public TimbnNpcDay Build()
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			return new TimbnNpcDay().Block("morning", 1f / 3f, (Action<TimbnNpcBlock>)delegate(TimbnNpcBlock block)
			{
				block.Origin("RT_tavern_owner_spawn").Intro((Func<TimbnNpcSteps, TimbnNpcSteps>)((TimbnNpcSteps steps) => steps.FireEvent("tp_to_tavern"))).LateIntro((Func<TimbnNpcSteps, TimbnNpcSteps>)((TimbnNpcSteps steps) => steps.FireEvent("tp_to_tavern")))
					.Loop((Func<TimbnNpcSteps, TimbnNpcSteps>)((TimbnNpcSteps steps) => steps.WaitGameMinutes(20f, 40f).Decide((Func<TimbnNpc, TimbnNpcSteps>)((TimbnNpc npc) => Pick(npc, "morning", "table one", "table two")))));
			}).Block("afternoon", 0.625f, (Action<TimbnNpcBlock>)delegate(TimbnNpcBlock block)
			{
				block.Origin("port_linda_start").Intro((Func<TimbnNpcSteps, TimbnNpcSteps>)ToEntrance).LateIntro((Func<TimbnNpcSteps, TimbnNpcSteps>)((TimbnNpcSteps steps) => steps.FireEvent("tp_to_tavern_entrance")))
					.Loop((Func<TimbnNpcSteps, TimbnNpcSteps>)((TimbnNpcSteps steps) => steps.WaitGameMinutes(20f, 40f).Decide((Func<TimbnNpc, TimbnNpcSteps>)((TimbnNpc npc) => Pick(npc, "afternoon", "cat")))));
			}).Block("night", 0.75f, (Action<TimbnNpcBlock>)delegate(TimbnNpcBlock block)
			{
				block.Origin("RT_tavern_owner_spawn").Intro((Func<TimbnNpcSteps, TimbnNpcSteps>)((TimbnNpcSteps steps) => steps.Walk("port_linda_player_spot").WalkNavmesh("RT_tavern_owner_spawn").Face((Direction)4))).LateIntro((Func<TimbnNpcSteps, TimbnNpcSteps>)((TimbnNpcSteps steps) => steps.FireEvent("tp_to_tavern")))
					.Loop((Func<TimbnNpcSteps, TimbnNpcSteps>)((TimbnNpcSteps steps) => steps.WaitGameMinutes(20f, 40f).Decide((Func<TimbnNpc, TimbnNpcSteps>)((TimbnNpc npc) => Pick(npc, "night", "table one")))));
			})
				.Block("late", 0f, (Action<TimbnNpcBlock>)delegate(TimbnNpcBlock block)
				{
					block.Intro((Func<TimbnNpcSteps, TimbnNpcSteps>)((TimbnNpcSteps steps) => steps.Decide((Func<TimbnNpc, TimbnNpcSteps>)((TimbnNpc _) => (!ConcertDone()) ? null : Sing())))).LateIntro((Func<TimbnNpcSteps, TimbnNpcSteps>)((TimbnNpcSteps steps) => steps.FireEvent("tp_to_tavern").Decide((Func<TimbnNpc, TimbnNpcSteps>)((TimbnNpc _) => (!ConcertDone()) ? null : Sing())))).Loop((Func<TimbnNpcSteps, TimbnNpcSteps>)((TimbnNpcSteps steps) => steps.WaitGameMinutes(20f, 40f).Decide((Func<TimbnNpc, TimbnNpcSteps>)((TimbnNpc npc) => (!ConcertDone()) ? Pick(npc, "night", "table one") : null))));
				})
				.Block("bed", 1f / 12f, (Action<TimbnNpcBlock>)delegate(TimbnNpcBlock block)
				{
					block.Intro((Func<TimbnNpcSteps, TimbnNpcSteps>)((TimbnNpcSteps steps) => steps.RunCode((Action<TimbnNpc>)delegate
					{
						_performer = null;
					}).WalkNavmesh("port_concert_doctor_scene").Walk("tavern_port_road_left_01")
						.WalkNavmesh(-104.35f, -338.61f)
						.Face((Direction)2)
						.FireEvent("tp_to_spot"))).LateIntro((Func<TimbnNpcSteps, TimbnNpcSteps>)((TimbnNpcSteps steps) => steps.RunCode((Action<TimbnNpc>)delegate
					{
						_performer = null;
					}).FireEvent("tp_to_spot")));
				})
				.OnlyWhile((Func<bool>)(() => (int)TimbnQuests.StatusOf("29_port_linda_meet").GetValueOrDefault() == 3))
				.ResumeWhenAt("RT_tavern_owner_spawn")
				.Doorway("RT_tavern_owner_spawn", "village_letter_owner_spawn", (TimbnWalk)2)
				.LogSteps((Func<bool>)(() => PluginConfig.WriteDebugLog.Value));
		}

		public void Tick()
		{
			//IL_001c: Unknown result type (might be due to invalid IL or missing references)
			TimbnNpc performer = _performer;
			int num;
			if (performer != null && !performer.IsGone)
			{
				WorldZoneData zone = TimbnPlayer.Zone;
				if (zone != null)
				{
					num = (TimbnWorld.ZonesAt(performer.Position, (GameSceneData)null).Contains(zone) ? 1 : 0);
					goto IL_0030;
				}
			}
			num = 0;
			goto IL_0030;
			IL_0030:
			bool flag = (byte)num != 0;
			if (flag && _song == null)
			{
				_song = ((TimbnFrameworkPlugin)_plugin).Audio.PlayMusic("violin");
				Log("Her violin fills the club.");
			}
			else if (!flag && _song != null)
			{
				StopSong();
			}
		}

		public void Reset()
		{
			_performer = null;
			StopSong();
		}

		private void StopSong()
		{
			_song?.Dispose();
			_song = null;
		}

		private static bool ConcertDone()
		{
			//IL_000d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0013: Invalid comparison between Unknown and I4
			return (int)TimbnQuests.StatusOf("68_port_concert_start").GetValueOrDefault() == 3;
		}

		private static TimbnNpcSteps ToEntrance(TimbnNpcSteps steps)
		{
			return steps.WalkNavmesh("linda_cough_refugees_linda_02").Walk("tavern_port_road_left_02").WalkNavmesh("port_linda_start")
				.Face((Direction)4);
		}

		private TimbnNpcSteps Sing()
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			return new TimbnNpcSteps().WalkNavmesh("port_concert_linda_start").Walk("port_concert_linda_spot").Animate("custom_loop_01_enter")
				.Face((Direction)4)
				.RunCode((Action<TimbnNpc>)delegate(TimbnNpc npc)
				{
					_performer = npc;
				});
		}

		private static TimbnNpcSteps TableTwo()
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			return new TimbnNpcSteps().Walk("village_letter_owner_table").WalkNavmesh("port_concert_villager_03_spot").Animate("custom_02")
				.Face((Direction)4)
				.WaitGameMinutes(30f, 60f)
				.WalkNavmesh("tavern_port_road_right_06")
				.Walk("port_linda_player_spot")
				.WalkNavmesh("RT_tavern_owner_spawn")
				.Face((Direction)4);
		}

		private static TimbnNpcSteps TableOne()
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			return new TimbnNpcSteps().WalkNavmesh("linda_cough_refugees_linda_02").WalkNavmesh("tavern_port_road_left_07").Animate("custom_02")
				.Face((Direction)4)
				.WaitGameMinutes(30f, 60f)
				.WalkNavmesh("tavern_port_road_right_06")
				.Walk("port_linda_player_spot")
				.WalkNavmesh("RT_tavern_owner_spawn")
				.Face((Direction)4);
		}

		private static TimbnNpcSteps Cat()
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			return new TimbnNpcSteps().Walk("port_linda_02").WalkNavmesh("tavern_port_road_left_01").WalkNavmesh(-104.35f, -338.61f)
				.Face((Direction)2)
				.WaitGameMinutes(15f, 30f)
				.WalkNavmesh("tavern_port_road_left_01")
				.WalkNavmesh("port_linda_02")
				.Walk("port_linda_start");
		}

		private TimbnNpcSteps? Pick(TimbnNpc npc, string block, params string[] trips)
		{
			double minutesLeftInBlock = npc.MinutesLeftInBlock;
			if (minutesLeftInBlock < 90.0)
			{
				Log($"Staying put in the {block}, the next block starts in {minutesLeftInBlock:0} minutes.");
				return null;
			}
			double num = default(double);
			Random random = npc.RollChance(0.75f, ref num, "a trip");
			if (random == null)
			{
				return null;
			}
			string text = trips[random.Next(trips.Length)];
			Log($"Rolled {num:0.00}, the {text} trip.");
			if (!(text == "table one"))
			{
				if (text == "table two")
				{
					return TableTwo();
				}
				return Cat();
			}
			return TableOne();
		}

		private static void Log(string line)
		{
			if (PluginConfig.WriteDebugLog.Value)
			{
				TimbnFrameworkPlugin<Plugin>.Logger.LogDebug((object)$"day {TimbnClock.Day} {TimbnClock.TimeText} Linda: {line}");
			}
		}
	}
	internal static class VendorHours
	{
		public const float Working = 0.25f;

		public const float Evening = 0.8f;

		public const float Night = 11f / 12f;

		public static bool IsClosed()
		{
			float timeOfDay = TimbnClock.TimeOfDay;
			if (timeOfDay < 0.25f || timeOfDay >= 0.8f)
			{
				return true;
			}
			return false;
		}
	}
	internal sealed class ChurchVisits
	{
		private const string _arrive = "donkey_01";

		private const string _road = "donkey_04";

		private const string _gate = "gd_graveyard_weed_4";

		private const float _opens = 0.25f;

		private const float _closes = 13f / 24f;

		private const float _everyoneLeaves = 2f / 3f;

		private const float _sermonDayCrowd = 1.5f;

		private const float _rainShare = 0.5f;

		private const float _stormShare = 0.25f;

		private const int _shortestStay = 240;

		private const int _longestStay = 360;

		private const int _funeralShortestGap = 2;

		private const int _funeralLongestGap = 10;

		private const float _groupSpacing = 3f;

		private const float _groupSpacingJitter = 1f;

		private const int _capacitySpare = 3;

		private const float _preloadAt = 1f / 12f;

		private const int _preloadCopies = 2;

		private static readonly string[] _homeGroups = new string[5] { "villagers_day_all", "town_A1_day_all", "town_A2_day_all", "town_A3_day_all", "town_A4_day_all" };

		private static readonly string[] _heavyRain = new string[3] { "Strong", "Storm", "Thunder" };

		private readonly Plugin _plugin;

		private readonly GraveyardVisitors _driver = new GraveyardVisitors();

		private readonly Funeral _funeral;

		private readonly List<TimbnNpc> _visitors = new List<TimbnNpc>();

		private readonly Random _random = new Random();

		private readonly List<(double At, int Size)> _funeralArrivals = new List<(double, int)>();

		private readonly List<(double At, string Kind, double LeaveAt)> _waiting = new List<(double, string, double)>();

		private int _crowdDay = -1;

		private int _preloadDay = -1;

		private bool _plannedForFuneral;

		private int _toArrive;

		private int _settled;

		private double _nextArrival;

		private double _lastMinutes;

		private bool IsOpen
		{
			get
			{
				if (TimbnClock.TimeOfDay >= 0.25f)
				{
					return TimbnClock.TimeOfDay < (_funeral.IsOn ? 0.375f : (13f / 24f));
				}
				return false;
			}
		}

		private static bool IsSermonDay => (int)TimbnClock.Weekday.GetValueOrDefault() == 5;

		private static double EveryoneLeavesAt => ((double)TimbnClock.Day + 0.6666666865348816) * 24.0 * 60.0;

		private static float WeatherShare
		{
			get
			{
				if (TimbnWorld.IsRaining)
				{
					if (!_heavyRain.Any((string heavy) => TimbnWorld.Weather.Contains(heavy)))
					{
						return 0.5f;
					}
					return 0.25f;
				}
				return 1f;
			}
		}

		private bool VisitsOn => ((TimbnFrameworkPlugin)_plugin).Settings.IsEnabled(PluginConfig.ChurchVisits);

		public ChurchVisits(Plugin plugin)
		{
			_plugin = plugin;
			_funeral = new Funeral(plugin, _driver)
			{
				MoreComing = () => _toArrive > 0 || _waiting.Count > 0
			};
		}

		public void Tick()
		{
			if (!TimbnGame.IsInGame || (!VisitsOn && !((TimbnFrameworkPlugin)_plugin).Settings.IsEnabled(PluginConfig.Funerals)))
			{
				return;
			}
			foreach (TimbnNpc item in _visitors.Where((TimbnNpc npc) => npc.IsGone).ToList())
			{
				_visitors.Remove(item);
				_driver.Forget(item);
				_funeral.Forget(item);
			}
			foreach (TimbnNpc item2 in _visitors.Where((TimbnNpc npc) => !npc.IsRunning).ToList())
			{
				GraveyardVisitors.Log(item2.Id + " was taken over by the game, so they are sent away.");
				Leave(item2);
			}
			double minutes = TimbnClock.Minutes;
			if (minutes < _lastMinutes)
			{
				_nextArrival = minutes;
			}
			_lastMinutes = minutes;
			_funeral.Check();
			if (_preloadDay != TimbnClock.Day && TimbnClock.TimeOfDay >= 1f / 12f)
			{
				_preloadDay = TimbnClock.Day;
				((TimbnFrameworkPlugin)_plugin).Npcs.Preload(Kinds(), 2);
			}
			if (_waiting.Count > 0 && minutes >= _waiting[0].At)
			{
				SpawnNext();
			}
			else
			{
				if (!IsOpen)
				{
					return;
				}
				if (_crowdDay != TimbnClock.Day)
				{
					PlanCrowd();
					return;
				}
				if (_funeral.IsOn && !_plannedForFuneral)
				{
					Replan("a funeral started after today's crowd was planned");
				}
				bool isOn = _funeral.IsOn;
				if (_toArrive <= 0)
				{
					return;
				}
				bool num;
				if (!isOn)
				{
					num = minutes < _nextArrival;
				}
				else
				{
					if (_funeralArrivals.Count == 0)
					{
						return;
					}
					num = minutes < _funeralArrivals[0].At;
				}
				if (num)
				{
					return;
				}
				List<string> list = (from _ in Kinds()
					orderby _random.Next()
					select _).ToList();
				if (list.Count == 0)
				{
					_toArrive = 0;
					GraveyardVisitors.Log("nobody lives in the village or the town yet, so nobody comes today.");
					return;
				}
				int num2;
				if (isOn)
				{
					num2 = Mathf.Min(_toArrive, _funeralArrivals[0].Size);
					_funeralArrivals.RemoveAt(0);
				}
				else
				{
					num2 = Mathf.Min(_toArrive, GroupSize());
					_nextArrival = minutes + (double)_random.Next(5, 31);
				}
				if (!isOn && _random.NextDouble() >= (double)WeatherShare)
				{
					_toArrive -= num2;
					_settled += num2;
					GraveyardVisitors.Log($"{num2} visitor(s) stay home in the {TimbnWorld.Weather}.");
				}
				else
				{
					Arrive(num2, list);
					_toArrive -= num2;
					_settled += num2;
					SpawnNext();
				}
			}
		}

		public void Prepare()
		{
			if (VisitsOn || ((TimbnFrameworkPlugin)_plugin).Settings.IsEnabled(PluginConfig.Funerals))
			{
				((TimbnFrameworkPlugin)_plugin).Npcs.Preload(Kinds(), 2);
				_driver.Paths.Rebuild();
				_driver.Paths.Warm();
			}
		}

		public void Replan()
		{
			Replan("the settings changed");
		}

		private static IEnumerable<string> Kinds()
		{
			return ((IEnumerable<string>)_homeGroups).SelectMany((Func<string, IEnumerable<string>>)TimbnNpcs.GroupMemberIds).Distinct();
		}

		private void Replan(string why)
		{
			if (TimbnGame.IsInGame && _crowdDay == TimbnClock.Day)
			{
				_plannedForFuneral = _funeral.IsOn;
				_toArrive = Math.Max(0, TodaysCrowd() - _settled);
				if (_funeral.IsOn)
				{
					ScheduleFuneralArrivals();
				}
				GraveyardVisitors.Log($"{why}, {_toArrive} more visitor(s) today.");
			}
		}

		public void SendAway()
		{
			foreach (TimbnNpc visitor in _visitors)
			{
				GraveyardVisitors.Log(visitor.Id + " is sent away.");
				visitor.Dispose();
			}
			_visitors.Clear();
			_waiting.Clear();
			_driver.Clear();
			_funeral.Clear();
		}

		public void Reset()
		{
			SendAway();
			_toArrive = 0;
			_crowdDay = -1;
			_preloadDay = -1;
		}

		private int TodaysCrowd()
		{
			int value = PluginConfig.MinimumChurchVisitors.Value;
			int num = Mathf.Max(value, PluginConfig.MaximumChurchVisitors.Value);
			int num2 = (VisitsOn ? Mathf.Clamp(TimbnWorld.TownGratitude / PluginConfig.GratitudePerVisitor.Value, value, num) : 0);
			if (_funeral.IsOn)
			{
				return Mathf.Min(Mathf.Max(num2, 5), Funeral.Capacity);
			}
			if (IsSermonDay)
			{
				num2 = Mathf.Min(Mathf.FloorToInt((float)num2 * 1.5f), num);
			}
			return Mathf.Min(num2, Mathf.Max(value, _driver.Capacity() + 3));
		}

		private TimbnNpcSteps WayOut(TimbnNpc npc)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0006: Expected O, but got Unknown
			//IL_0022: Unknown result type (might be due to invalid IL or missing references)
			TimbnNpcSteps val = new TimbnNpcSteps();
			GDPointData val2 = TimbnWorld.Point("donkey_04");
			if (val2 != null)
			{
				_driver.Paths.Walk(npc, val, val2.Position);
			}
			else
			{
				val.WalkNavmesh("donkey_04");
			}
			return val.Walk("donkey_01");
		}

		private void PlanCrowd()
		{
			//IL_0081: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00cc: Unknown result type (might be due to invalid IL or missing references)
			_crowdDay = TimbnClock.Day;
			_plannedForFuneral = _funeral.IsOn;
			_settled = 0;
			_toArrive = TodaysCrowd();
			_nextArrival = TimbnClock.Minutes;
			if (_funeral.IsOn)
			{
				ScheduleFuneralArrivals();
			}
			_driver.Paths.Rebuild();
			GraveyardPaths paths = _driver.Paths;
			GDPointData obj = TimbnWorld.Point("gd_graveyard_weed_4");
			paths.Entrance = ((obj != null) ? new Vector3?(obj.Position) : ((Vector3?)null));
			GraveyardVisitors.Log(string.Format("town gratitude {0}{1}, {2} visitor(s) today.", TimbnWorld.TownGratitude, IsSermonDay ? ", the sermon day" : "", _toArrive));
			Rect graveyardBounds = TimbnWorld.GraveyardBounds;
			GraveyardVisitors.Log($"the graveyard runs x {((Rect)(ref graveyardBounds)).xMin:0.0} to {((Rect)(ref graveyardBounds)).xMax:0.0}, z {((Rect)(ref graveyardBounds)).yMin:0.0} to {((Rect)(ref graveyardBounds)).yMax:0.0}.");
		}

		private void ScheduleFuneralArrivals()
		{
			_funeralArrivals.Clear();
			double num = Math.Max(TimbnClock.Minutes, ((double)TimbnClock.Day + 0.25) * 24.0 * 60.0);
			double num2 = Math.Max(num, ((double)TimbnClock.Day + 0.375) * 24.0 * 60.0 - 1.0);
			List<int> list = new List<int>();
			List<double> list2 = new List<double>();
			int num3 = _toArrive;
			while (num3 > 0)
			{
				int num4 = Math.Min(num3, FuneralGroupSize());
				list.Add(num4);
				list2.Add(2.0 + _random.NextDouble() * 8.0);
				num3 -= num4;
			}
			double num5 = Math.Min(1.0, (num2 - num) / Math.Max(1.0, list2.Skip(1).Sum()));
			double num6 = num;
			for (int i = 0; i < list.Count; i++)
			{
				if (i > 0)
				{
					num6 += list2[i] * num5;
				}
				_funeralArrivals.Add((num6, list[i]));
			}
			GraveyardVisitors.Log(string.Format("funeral, {0} mourner(s) come in groups of {1}.", _toArrive, string.Join(", ", _funeralArrivals.Select(((double At, int Size) arrival) => arrival.Size))));
		}

		private int FuneralGroupSize()
		{
			double num = _random.NextDouble();
			if (!(num < 0.5))
			{
				if (!(num < 0.8))
				{
					if (!(num < 0.93))
					{
						return _random.Next(4, 6);
					}
					return 3;
				}
				return 2;
			}
			return 1;
		}

		private int GroupSize()
		{
			double num = _random.NextDouble();
			if (!(num < 0.6))
			{
				if (!(num < 0.9))
				{
					return 3;
				}
				return 2;
			}
			return 1;
		}

		private void Arrive(int size, IReadOnlyList<string> kinds)
		{
			double minutes = TimbnClock.Minutes;
			double item = Math.Min(minutes + (double)_random.Next(240, 361), EveryoneLeavesAt);
			for (int i = 0; i < size; i++)
			{
				double item2 = ((i == 0) ? minutes : (minutes + (double)((float)i * 3f) + _random.NextDouble() * 1.0));
				_waiting.Add((item2, kinds[i % kinds.Count], item));
			}
			_waiting.Sort(((double At, string Kind, double LeaveAt) first, (double At, string Kind, double LeaveAt) second) => first.At.CompareTo(second.At));
		}

		private void SpawnNext()
		{
			//IL_00a2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a8: Expected O, but got Unknown
			(double, string, double) tuple = _waiting[0];
			string item = tuple.Item2;
			double leaveAt = tuple.Item3;
			_waiting.RemoveAt(0);
			TimbnNpc npc = ((TimbnFrameworkPlugin)_plugin).Npcs.Spawn(item, "donkey_01");
			if (npc == null)
			{
				_waiting.Clear();
				_toArrive = 0;
				GraveyardVisitors.Log("nobody could be added at the donkey, so nobody else comes today.");
				return;
			}
			npc.CollidesWithPlayer = false;
			_visitors.Add(npc);
			double setOff = TimbnClock.Minutes;
			TimbnNpcSteps val = new TimbnNpcSteps();
			val.Walk("donkey_04").RunCode((Action<TimbnNpc>)delegate(TimbnNpc visitor)
			{
				GraveyardVisitors.Log(string.Format("{0} reaches {1} {2:0} minutes after arriving.", visitor.Id, "donkey_04", TimbnClock.Minutes - setOff));
			}).Decide((Func<TimbnNpc, TimbnNpcSteps>)((TimbnNpc visitor) => (TimbnNpcSteps?)((!_funeral.IsOn) ? ((object)_driver.Welcome(visitor, leaveAt, WayOut)) : ((object)_funeral.Welcome(visitor, WayOut)))));
			npc.RunSteps(val, (Action)delegate
			{
				Leave(npc);
			});
			GraveyardVisitors.Log(npc.Id + " arrives at donkey_01.");
		}

		private void Leave(TimbnNpc npc)
		{
			GraveyardVisitors.Log(npc.Id + " leaves.");
			_visitors.Remove(npc);
			_driver.Forget(npc);
			_funeral.Forget(npc);
			npc.Dispose();
		}
	}
	internal sealed class GraveyardVisitors
	{
		private sealed class Visitor
		{
			public double LeaveAt { get; }

			public Func<TimbnNpc, TimbnNpcSteps> WayOut { get; }

			public IGraveyardStop? Last { get; set; }

			public Visitor(double leaveAt, Func<TimbnNpc, TimbnNpcSteps> wayOut)
			{
				LeaveAt = leaveAt;
				WayOut = wayOut;
			}
		}

		private static readonly string[] _monumentIds = new string[3] { "graveyard_monument_0", "graveyard_monument_1", "graveyard_monument_2" };

		private static readonly string[] _columbariumIds = new string[2] { "graveyard_columbarium_1", "graveyard_columbarium_2" };

		private static readonly string[] _cryptIds = new string[1] { "graveyard_crypt" };

		private static readonly string[] _flowerBedIds = new string[3] { "graveyard_flowerbed_1", "graveyard_flowerbed_2", "graveyard_flowerbed_3" };

		private readonly Dictionary<TimbnNpc, Visitor> _visitors = new Dictionary<TimbnNpc, Visitor>();

		private readonly Dictionary<TimbnNpc, Vector3> _taken = new Dictionary<TimbnNpc, Vector3>();

		private readonly Dictionary<TimbnNpc, WgoData> _claimed = new Dictionary<TimbnNpc, WgoData>();

		private readonly IGraveyardStop _wander = new WanderStop();

		private readonly IGraveyardStop[] _stops;

		public StandByStop GraveStop { get; }

		public Random Random { get; } = new Random();

		public GraveyardPaths Paths { get; } = new GraveyardPaths();

		public IEnumerable<Vector3> Taken => _taken.Values;

		public GraveyardVisitors()
		{
			GraveStop = new StandByStop("grave", "nobody is buried in the graveyard yet", (Func<IEnumerable<WgoData>>)TimbnWorld.GravesWithBodies, 90, 150);
			_stops = new IGraveyardStop[8]
			{
				GraveStop,
				new StandByStop("monument", "no monument stands in the graveyard yet", () => ((IEnumerable<string>)_monumentIds).SelectMany((Func<string, IEnumerable<WgoData>>)TimbnWorld.FindAll), 20, 40),
				new StandByStop("columbarium", "no columbarium stands in the graveyard yet", () => ((IEnumerable<string>)_columbariumIds).SelectMany((Func<string, IEnumerable<WgoData>>)TimbnWorld.FindAll), 30, 60),
				new StandByStop("crypt", "no crypt stands in the graveyard yet", () => ((IEnumerable<string>)_cryptIds).SelectMany((Func<string, IEnumerable<WgoData>>)TimbnWorld.FindAll), 30, 60),
				new StandByStop("flower bed", "no flower bed grows in the graveyard yet", () => ((IEnumerable<string>)_flowerBedIds).SelectMany((Func<string, IEnumerable<WgoData>>)TimbnWorld.FindAll), 10, 30),
				new BenchStop(),
				new ChurchStop(),
				_wander
			};
		}

		public int Capacity()
		{
			return _stops.Sum((IGraveyardStop stop) => stop.Capacity());
		}

		public static void Log(string line)
		{
			if (PluginConfig.WriteDebugLog.Value)
			{
				TimbnFrameworkPlugin<Plugin>.Logger.LogDebug((object)$"day {TimbnClock.Day} {TimbnClock.TimeText} Church visits: {line}");
			}
		}

		public static string Flat(Vector3 spot)
		{
			//IL_0005: Unknown result type (might be due to invalid IL or missing references)
			//IL_0010: Unknown result type (might be due to invalid IL or missing references)
			return $"({spot.x:0.0}, {spot.z:0.0})";
		}

		public TimbnNpcSteps? Welcome(TimbnNpc npc, double leaveAt, Func<TimbnNpc, TimbnNpcSteps> wayOut)
		{
			_visitors[npc] = new Visitor(leaveAt, wayOut);
			return Next(npc);
		}

		public void Take(TimbnNpc npc, Vector3 spot)
		{
			//IL_0007: Unknown result type (might be due to invalid IL or missing references)
			_taken[npc] = spot;
		}

		public void Claim(TimbnNpc npc, WgoData target)
		{
			_claimed[npc] = target;
		}

		public bool IsClaimed(WgoData target)
		{
			return _claimed.ContainsValue(target);
		}

		public void Forget(TimbnNpc npc)
		{
			_visitors.Remove(npc);
			_taken.Remove(npc);
			_claimed.Remove(npc);
		}

		public void Clear()
		{
			_visitors.Clear();
			_taken.Clear();
			_claimed.Clear();
		}

		private TimbnNpcSteps? Next(TimbnNpc npc)
		{
			//IL_012c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0151: Unknown result type (might be due to invalid IL or missing references)
			_taken.Remove(npc);
			_claimed.Remove(npc);
			if (!_visitors.TryGetValue(npc, out Visitor visitor))
			{
				return null;
			}
			if (TimbnClock.Minutes >= visitor.LeaveAt)
			{
				Log(npc.Id + " heads home.");
				return visitor.WayOut(npc);
			}
			int val = (int)Math.Ceiling(visitor.LeaveAt - TimbnClock.Minutes);
			List<IGraveyardStop> list = _stops.Where((IGraveyardStop stop) => stop != visitor.Last || stop == _wander).ToList();
			while (list.Count > 0)
			{
				IGraveyardStop graveyardStop = list[Random.Next(list.Count)];
				int stay = Math.Max(1, Math.Min(graveyardStop.RollStay(Random), val));
				TimbnNpcSteps val2 = graveyardStop.Plan(npc, this, stay);
				if (val2 != null)
				{
					visitor.Last = graveyardStop;
					return val2.Decide((Func<TimbnNpc, TimbnNpcSteps>)Next);
				}
				list.Remove(graveyardStop);
			}
			Log(npc.Id + " finds nothing to do and waits at " + Flat(npc.Position) + ".");
			visitor.Last = null;
			return new TimbnNpcSteps().WaitGameMinutes(5f, 15f).Decide((Func<TimbnNpc, TimbnNpcSteps>)Next);
		}
	}
	internal sealed class GraveyardPaths
	{
		internal sealed class Segment
		{
			public bool Horizontal { get; }

			public bool Main { get; set; }

			public float Line { get; }

			public float Height { get; }

			public float Start { get; set; }

			public float End { get; set; }

			public float Length => End - Start;

			public Segment(bool horizontal, float line, float height, float start, float end)
			{
				Horizontal = horizontal;
				Line = line;
				Height = height;
				Start = start;
				End = end;
			}

			public Vector3 At(float along)
			{
				//IL_0028: Unknown result type (might be due to invalid IL or missing references)
				//IL_0015: Unknown result type (might be due to invalid IL or missing references)
				if (!Horizontal)
				{
					return new Vector3(Line, Height, along);
				}
				return new Vector3(along, Height, Line);
			}

			public float Along(Vector3 point)
			{
				//IL_0010: Unknown result type (might be due to invalid IL or missing references)
				//IL_0008: Unknown result type (might be due to invalid IL or missing references)
				return Mathf.Clamp(Horizontal ? point.x : point.z, Start, End);
			}

			public Vector3 Nearest(Vector3 point)
			{
				//IL_0002: Unknown result type (might be due to invalid IL or missing references)
				//IL_0008: Unknown result type (might be due to invalid IL or missing references)
				return At(Along(point));
			}

			public bool Contains(float along, float slack)
			{
				if (along >= Start - slack)
				{
					return along <= End + slack;
				}
				return false;
			}
		}

		private sealed class Tile
		{
			public string Name { get; }

			public Vector3 Position { get; }

			public bool Mirrored { get; }

			public Bounds Bounds { get; }

			public Tile(string name, Vector3 position, bool mirrored, Bounds bounds)
			{
				//IL_000e: Unknown result type (might be due to invalid IL or missing references)
				//IL_000f: Unknown result type (might be due to invalid IL or missing references)
				//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)
				Name = name;
				Position = position;
				Mirrored = mirrored;
				Bounds = bounds;
			}
		}

		private const string _roadsPath = "World/StaticContent/CemeteryZone/Roads";

		private const float _sameLine = 0.2f;

		private const float _touching = 0.1f;

		private const float _junctionReach = 1f;

		private const float _endInset = 1f;

		private const float _acrossDirt = 0.3f;

		private const float _upAndDownDirt = 0.16f;

		private const float _groundSnap = 0.1f;

		private const float _groundStep = 0.1f;

		private const int _groundTries = 20;

		private const int _spotTries = 10;

		private const float _mainPathCost = 0.5f;

		private const float _upAndDownCost = 2.5f;

		private const float _crowdedCost = 1000f;

		private const float _crowdReach = 0.5f;

		private const float _acrossReach = 2f;

		private readonly List<Segment> _segments = new List<Segment>();

		public Func<TimbnNpc, IReadOnlyList<Vector2>>? Avoid { get; set; }

		public Vector3? Entrance { get; set; }

		public void Rebuild()
		{
			//IL_0043: Unknown result type (might be due to invalid IL or missing references)
			//IL_004a: Expected O, but got Unknown
			//IL_006d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0074: Expected O, but got Unknown
			_segments.Clear();
			GameObject val = GameObject.Find("World/StaticContent/CemeteryZone/Roads");
			if (val == null)
			{
				GraveyardVisitors.Log("no World/StaticContent/CemeteryZone/Roads in the scene, so visitors walk straight over the grass.");
				return;
			}
			List<Tile> list = new List<Tile>();
			foreach (Transform item in val.transform)
			{
				Transform val2 = item;
				if (!((Component)val2).gameObject.activeSelf)
				{
					continue;
				}
				foreach (Transform item2 in val2)
				{
					Transform val3 = item2;
					if (((Component)val3).gameObject.activeSelf)
					{
						list.AddRange(from tile in ((Component)val3).GetComponentsInChildren<SpriteRenderer>(true)
							where (Object)(object)tile.sprite != (Object)null
							select new Tile(Regex.Replace(((Object)tile).name, " \\(\\d+\\)$", ""), ((Component)tile).transform.position, ((Component)tile).transform.lossyScale.x < 0f, WorldBounds(tile)));
					}
				}
			}
			float height = ((list.Count > 0) ? list.Average((Tile tile) => tile.Position.y) : 0f);
			List<Segment> list2 = Lines(list, height, horizontal: true);
			List<Segment> list3 = Lines(list, height, horizontal: false);
			foreach (Segment item3 in list2)
			{
				Trim(item3, list3);
			}
			foreach (Segment item4 in list3)
			{
				Trim(item4, list2);
			}
			_segments.AddRange(from segment in list2.Concat(list3)
				where segment.End - segment.Start > 0.1f
				select segment);
			GraveyardVisitors.Log($"the graveyard paths are {list2.Count} across and {list3.Count} up and down, from {list.Count} tiles.");
		}

		public void Warm()
		{
			//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_001a: 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)
			Rect graveyardBounds = TimbnWorld.GraveyardBounds;
			Route(new Vector3(((Rect)(ref graveyardBounds)).xMin, 0f, ((Rect)(ref graveyardBounds)).yMin), new Vector3(((Rect)(ref graveyardBounds)).xMax, 0f, ((Rect)(ref graveyardBounds)).yMax), Array.Empty<Vector2>());
		}

		public TimbnNpcSteps Walk(TimbnNpc npc, TimbnNpcSteps steps, Vector3 to)
		{
			//IL_0023: Unknown result type (might be due to invalid IL or missing references)
			//IL_0028: Unknown result type (might be due to invalid IL or missing references)
			//IL_003a: Unknown result type (might be due to invalid IL or missing references)
			//IL_003f: 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_0041: Unknown result type (might be due to invalid IL or missing references)
			//IL_000e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0014: Unknown result type (might be due to invalid IL or missing references)
			//IL_007d: Unknown result type (might be due to invalid IL or missing references)
			//IL_007e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0055: Unknown result type (might be due to invalid IL or missing references)
			//IL_005a: Unknown result type (might be due to invalid IL or missing references)
			//IL_005c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0072: Unknown result type (might be due to invalid IL or missing references)
			//IL_0068: Unknown result type (might be due to invalid IL or missing references)
			//IL_0070: Unknown result type (might be due to invalid IL or missing references)
			//IL_0071: Unknown result type (might be due to invalid IL or missing references)
			//IL_007a: Unknown result type (might be due to invalid IL or missing references)
			//IL_007b: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d0: Unknown result type (might be due to invalid IL or missing references)
			if (_segments.Count == 0)
			{
				return steps.WalkNavmesh(to.x, to.z);
			}
			Vector3 from = npc.Position;
			List<Vector3> corners = new List<Vector3>();
			Vector3 val = from;
			Vector3 val2 = to;
			Vector3? entrance = Entrance;
			if (entrance.HasValue)
			{
				Vector3 valueOrDefault = entrance.GetValueOrDefault();
				if (!Inside(from))
				{
					Add(valueOrDefault);
					val = valueOrDefault;
				}
				if (!Inside(to))
				{
					val2 = valueOrDefault;
				}
			}
			foreach (Vector3 item in Route(val, val2, Avoid?.Invoke(npc)))
			{
				Add(item);
			}
			Add(val2);
			Add(to);
			if (corners.Count != 0)
			{
				return steps.WalkStraight((IReadOnlyList<Vector3>)corners);
			}
			return steps;
			void Add(Vector3 point)
			{
				//IL_0023: 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_0028: 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)
				Vector3 val3;
				if (corners.Count != 0)
				{
					List<Vector3> list = corners;
					val3 = list[list.Count - 1];
				}
				else
				{
					val3 = from;
				}
				if (TimbnWorld.FlatDistance(val3, point) >= 0.1f)
				{
					corners.Add(point);
				}
			}
		}

		private static bool Inside(Vector3 spot)
		{
			//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_0008: Unknown result type (might be due to invalid IL or missing references)
			//IL_000e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0014: Unknown result type (might be due to invalid IL or missing references)
			Rect graveyardBounds = TimbnWorld.GraveyardBounds;
			return ((Rect)(ref graveyardBounds)).Contains(new Vector2(spot.x, spot.z));
		}

		public bool TryRandomSpot(Vector3 from, float shortestHop, Random random, out Vector3 spot)
		{
			//IL_0002: 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_008a: Unknown result type (might be due to invalid IL or missing references)
			//IL_00aa: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ad: Unknown result type (might be due to invalid IL or missing references)
			spot = default(Vector3);
			float num = _segments.Sum((Segment segment) => segment.Length);
			if (num <= 0f)
			{
				return false;
			}
			for (int num2 = 0; num2 < 10; num2++)
			{
				float num3 = (float)random.NextDouble() * num;
				foreach (Segment segment in _segments)
				{
					if (num3 > segment.Length)
					{
						num3 -= segment.Length;
						continue;
					}
					spot = segment.At(segment.Start + num3);
					break;
				}
				if (TimbnWorld.FlatDistance(from, spot) >= shortestHop)
				{
					return true;
				}
			}
			return false;
		}

		public bool TryNearest(Vector3 spot, out Vector3 onPath, bool preferAcross = false)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0018: Unknown result type (might be due to invalid IL or missing references)
			//IL_001f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0024: Unknown result type (might be due to invalid IL or missing references)
			onPath = default(Vector3);
			if (_segments.Count == 0)
			{
				return false;
			}
			onPath = Nearest(spot, preferAcross).Point;
			return true;
		}

		public Vector3 PointAlong(Vector3 from, Vector3 to, float distance)
		{
			//IL_0001: 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_0024: 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_0021: 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_0040: Unknown result type (might be due to invalid IL or missing references)
			//IL_0092: Unknown result type (might be due to invalid IL or missing references)
			//IL_0053: Unknown result type (might be due to invalid IL or missing references)
			//IL_005a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0071: Unknown result type (might be due to invalid IL or missing references)
			List<Vector3> list = Route(from, to, null, across: true);
			if (list.Count == 0)
			{
				if (_segments.Count <= 0)
				{
					return from;
				}
				return Nearest(from, preferAcross: true).Point;
			}
			for (int i = 1; i < list.Count; i++)
			{
				float num = Vector3.Distance(list[i - 1], list[i]);
				if (distance <= num)
				{
					return Vector3.Lerp(list[i - 1], list[i], (num > 0f) ? (distance / num) : 0f);
				}
				distance -= num;
			}
			return list[list.Count - 1];
		}

		private static List<Segment> Lines(List<Tile> tiles, float height, bool horizontal)
		{
			string axis = (horizontal ? "_hor" : "_ver");
			List<float> sorted = (from tile in tiles
				where tile.Name.Contains(axis) && !tile.Name.Contains("_end")
				select Anchor(tile, horizontal) into result
				orderby result
				select result).ToList();
			List<Segment> list = new List<Segment>();
			foreach (float line in Cluster(sorted))
			{
				List<(float Start, float End, bool Main)> list2 = (from span in tiles.Where((Tile tile) => (tile.Name.Contains(axis) || tile.Name.Contains("cross")) && Covers(tile.Bounds, line, horizontal)).Select(delegate(Tile tile)
					{
						//IL_0045: Unknown result type (might be due to invalid IL or missing references)
						//IL_004a: Unknown result type (might be due to invalid IL or missing references)
						//IL_004d: Unknown result type (might be due to invalid IL or missing references)
						//IL_0058: Unknown result type (might be due to invalid IL or missing references)
						//IL_005d: 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_0009: Unknown result type (might be due to invalid IL or missing references)
						//IL_000e: Unknown result type (might be due to invalid IL or missing references)
						//IL_0011: Unknown result type (might be due to invalid IL or missing references)
						//IL_001c: Unknown result type (might be due to invalid IL or missing references)
						//IL_0021: Unknown result type (might be due to invalid IL or missing references)
						//IL_0024: Unknown result type (might be due to invalid IL or missing references)
						Bounds bounds;
						if (!horizontal)
						{
							bounds = tile.Bounds;
							float z = ((Bounds)(ref bounds)).min.z;
							bounds = tile.Bounds;
							return (Start: z, End: ((Bounds)(ref bounds)).max.z, Main: tile.Name.Contains("_big_"));
						}
						bounds = tile.Bounds;
						float x = ((Bounds)(ref bounds)).min.x;
						bounds = tile.Bounds;
						return (Start: x, End: ((Bounds)(ref bounds)).max.x, Main: tile.Name.Contains("_big_"));
					})
					orderby span.Start
					select span).ToList();
				Segment segment = null;
				foreach (var (num, num2, flag) in list2)
				{
					if (segment != null && num <= segment.End + 0.1f)
					{
						segment.End = Mathf.Max(segment.End, num2);
						segment.Main |= flag;
					}
					else
					{
						segment = new Segment(horizontal, line, height, num, num2)
						{
							Main = flag
						};
						list.Add(segment);
					}
				}
			}
			return list;
		}

		private static IEnumerable<float> Cluster(List<float> sorted)
		{
			List<float> group = new List<float>();
			foreach (float value in sorted)
			{
				if (group.Count > 0)
				{
					if (value - group[group.Count - 1] > 0.2f)
					{
						yield return group.Average();
						group.Clear();
					}
				}
				group.Add(value);
			}
			if (group.Count > 0)
			{
				yield return group.Average();
			}
		}

		private static Bounds WorldBounds(SpriteRenderer tile)
		{
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_000b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0016: 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_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_004d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0059: Unknown result type (might be due to invalid IL or missing references)
			//IL_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_0081: Unknown result type (might be due to invalid IL or missing references)
			//IL_008d: Unknown result type (might be due to invalid IL or missing references)
			//IL_009c: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ab: Unknown result type (might be due to invalid IL or missing references)
			Bounds bounds = tile.sprite.bounds;
			Bounds resul