Decompiled source of DiscoveryJournal v0.2.1

BepInEx/plugins/DiscoveryJournal.dll

Decompiled 3 days ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Text;
using System.Xml.Serialization;
using BepInEx;
using BepInEx.Configuration;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using TMPro;
using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.Events;
using UnityEngine.UI;

[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("Dedryx")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyCopyright("Copyright © Dedryx 2026")]
[assembly: AssemblyDescription("A journal for everything your character has learned how to craft or build.")]
[assembly: AssemblyFileVersion("0.2.1.0")]
[assembly: AssemblyInformationalVersion("0.2.1+9580edb5a8cf9dec42b82f9bd967714aed9fe990")]
[assembly: AssemblyProduct("DiscoveryJournal")]
[assembly: AssemblyTitle("DiscoveryJournal")]
[assembly: AssemblyVersion("0.2.1.0")]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)]
	internal sealed class RefSafetyRulesAttribute : Attribute
	{
		public readonly int Version;

		public RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
}
namespace DiscoveryJournal
{
	public sealed class JournalEntry
	{
		public string Id;

		public string KnowledgeKey;

		public string Name;

		public string Summary;

		public string Detail;

		public string[] MaterialKeys = Array.Empty<string>();

		public string[] MaterialNames = Array.Empty<string>();

		public string[] Locations = Array.Empty<string>();

		public string Category = "Other";

		public int BiomeMask;

		public bool HasUnresolvedBiome;

		public JournalKind Kind;

		public Sprite Icon;

		internal ItemData InspectItem;
	}
	internal static class JournalCatalog
	{
		private static readonly FieldRef<Player, Dictionary<string, int>> KnownStations = AccessTools.FieldRefAccess<Player, Dictionary<string, int>>("m_knownStations");

		internal static string Localize(string value)
		{
			if (Localization.instance != null)
			{
				return Localization.instance.Localize(value ?? "");
			}
			return value ?? "";
		}

		private static string Requirements(Requirement[] requirements, bool one = false, bool upgrader = false)
		{
			List<string> list = new List<string> { one ? "Requirements (one of):" : "Requirements:" };
			Requirement[] array = requirements ?? Array.Empty<Requirement>();
			foreach (Requirement val in array)
			{
				if (Object.op_Implicit((Object)(object)val?.m_resItem) && val.m_upgraderResource == upgrader && val.GetAmount(1) > 0)
				{
					list.Add(Localize(val.m_resItem.m_itemData.m_shared.m_name) + "    " + val.GetAmount(1));
				}
			}
			if (list.Count == 1)
			{
				list.Add("None");
			}
			return string.Join("\n", list);
		}

		private static string ItemCategory(ItemType type)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0002: Unknown result type (might be due to invalid IL or missing references)
			//IL_0068: Expected I4, but got Unknown
			switch (type - 1)
			{
			case 2:
			case 3:
			case 13:
			case 14:
			case 21:
				return "Weapons";
			case 5:
			case 6:
			case 10:
			case 11:
			case 16:
				return "Armor";
			case 4:
				return "Shields";
			case 1:
			case 20:
				return "Food & Consumables";
			case 8:
			case 22:
				return "Ammunition";
			case 18:
				return "Tools";
			case 0:
				return "Materials";
			case 17:
			case 23:
				return "Utility";
			default:
				return "Other Items";
			}
		}

		private static string[] MaterialKeys(Requirement[] requirements)
		{
			return (from r in requirements ?? Array.Empty<Requirement>()
				where Object.op_Implicit((Object)(object)r?.m_resItem) && !r.m_upgraderResource && r.GetAmount(1) > 0
				select r.m_resItem.m_itemData.m_shared.m_name).Distinct<string>(StringComparer.Ordinal).ToArray();
		}

		internal static List<JournalEntry> Read(Player player, NearbyBiomeResolver biomeResolver, RecipeBiomeMappings biomeMappings)
		{
			//IL_042e: Unknown result type (might be due to invalid IL or missing references)
			//IL_066b: Unknown result type (might be due to invalid IL or missing references)
			//IL_06b7: Unknown result type (might be due to invalid IL or missing references)
			//IL_06be: Invalid comparison between Unknown and I4
			biomeMappings.Begin();
			Dictionary<string, JournalEntry> dictionary = new Dictionary<string, JournalEntry>(StringComparer.Ordinal);
			List<ItemData> list = (from g in ObjectDB.instance.m_items
				where Object.op_Implicit((Object)(object)g)
				select g.GetComponent<ItemDrop>() into i
				where Object.op_Implicit((Object)(object)i)
				select i.m_itemData).Concat(((Humanoid)player).GetInventory().GetAllItems()).ToList();
			string[] array = (from s in (from p in (from t in list
						where Object.op_Implicit((Object)(object)t?.m_shared?.m_buildPieces)
						select t.m_shared.m_buildPieces).Distinct().SelectMany((PieceTable t) => t.m_pieces)
					where Object.op_Implicit((Object)(object)p)
					select p).SelectMany((GameObject p) => p.GetComponentsInChildren<CraftingStation>(true))
				where s.m_upgrader && KnownStations.Invoke(player).ContainsKey(s.m_name)
				select Localize(s.m_name)).Distinct().ToArray();
			foreach (Recipe recipe in ObjectDB.instance.m_recipes)
			{
				if (!Object.op_Implicit((Object)(object)recipe) || !Object.op_Implicit((Object)(object)recipe.m_item) || recipe.m_item.m_itemData?.m_shared == null)
				{
					continue;
				}
				string name = recipe.m_item.m_itemData.m_shared.m_name;
				string text = JournalRules.StableId(JournalKind.Crafting, ((Object)recipe).name, ((Object)((Component)recipe.m_item).gameObject).name);
				CraftingStation requiredStation = recipe.GetRequiredStation(1);
				string text2 = (Object.op_Implicit((Object)(object)requiredStation) ? Localize(requiredStation.m_name) : "No crafting station");
				Sprite[] icons = recipe.m_item.m_itemData.m_shared.m_icons;
				string[] array2 = MaterialKeys(recipe.m_resources);
				int inferred = NearbyBiomeResolver.RecipeBiome(array2.Select(biomeResolver.ResolveProgression).ToArray(), recipe.m_requireOnlyOneIngredient);
				bool overridden;
				int num = biomeMappings.Apply(text, inferred, out overridden);
				biomeMappings.Add(text, JournalKind.Crafting, ((Object)recipe).name + " / " + ((Object)((Component)recipe.m_item).gameObject).name, Localize(name), inferred, num, overridden, array2.Select(Localize));
				if (player.IsRecipeKnown(name))
				{
					dictionary[text] = new JournalEntry
					{
						Id = text,
						KnowledgeKey = name,
						Kind = JournalKind.Crafting,
						Name = Localize(name),
						Icon = ((icons != null && icons.Length != 0) ? icons[0] : null),
						Summary = text2,
						InspectItem = recipe.m_item.m_itemData,
						MaterialKeys = array2,
						MaterialNames = array2.Select(Localize).ToArray(),
						BiomeMask = num,
						HasUnresolvedBiome = (num == 0),
						Locations = new string[1] { text2 },
						Category = ItemCategory(recipe.m_item.m_itemData.m_shared.m_itemType),
						Detail = JournalRules.WithDescription(Localize(recipe.m_item.m_itemData.m_shared.m_description), "Craft at:\n" + text2 + (Object.op_Implicit((Object)(object)requiredStation) ? ("\n\nRequired station level:\n" + recipe.GetRequiredStationLevel(1)) : "") + "\n\n" + Requirements(recipe.m_resources, recipe.m_requireOnlyOneIngredient) + "\n\nBase output: " + recipe.m_amount + (recipe.m_noCraftOnlyUpgrade ? "\nUpgrade-only recipe" : "") + ((array.Length != 0 && recipe.m_resources != null && recipe.m_resources.Any((Requirement r) => Object.op_Implicit((Object)(object)r?.m_resItem) && r.m_upgraderResource && r.GetAmount(1) > 0)) ? ("\n\n" + string.Join(" / ", array) + "\n" + Requirements(recipe.m_resources, one: false, upgrader: true)) : ""))
					};
				}
			}
			HashSet<string> hashSet = new HashSet<string>(StringComparer.Ordinal);
			foreach (ItemData item in list)
			{
				PieceTable val = item?.m_shared?.m_buildPieces;
				if (!Object.op_Implicit((Object)(object)val))
				{
					continue;
				}
				string text3 = Localize(item.m_shared.m_name);
				if (!hashSet.Add(((Object)val).GetInstanceID() + ":" + text3))
				{
					continue;
				}
				foreach (GameObject piece in val.m_pieces)
				{
					if (!Object.op_Implicit((Object)(object)piece))
					{
						continue;
					}
					Piece component = piece.GetComponent<Piece>();
					if (!Object.op_Implicit((Object)(object)component))
					{
						continue;
					}
					string text4 = JournalRules.StableId(JournalKind.Building, ((Object)piece).name);
					int num2 = val.m_categories.IndexOf(component.m_category);
					string text5 = ((num2 >= 0 && num2 < val.m_categoryLabels.Count) ? Localize(val.m_categoryLabels[num2]) : ((object)Unsafe.As<PieceCategory, PieceCategory>(ref component.m_category)/*cast due to .constrained prefix*/).ToString());
					if ((int)component.m_category == 100 && val.m_categoryLabels.Count > 0)
					{
						text5 = string.Join(", ", val.m_categoryLabels.Select(Localize));
					}
					string text6 = text3 + " > " + text5;
					if (dictionary.TryGetValue(text4, out var value))
					{
						if (!value.Summary.Split('\n').Contains(text6))
						{
							JournalEntry journalEntry = value;
							journalEntry.Summary = journalEntry.Summary + "\n" + text6;
							JournalEntry journalEntry2 = value;
							journalEntry2.Detail = journalEntry2.Detail + "\n\nAlso build with:\n" + text6;
							List<string> list2 = new List<string> { text3 };
							if (Object.op_Implicit((Object)(object)component.m_craftingStation))
							{
								list2.Add(Localize(component.m_craftingStation.m_name));
							}
							value.Locations = value.Locations.Concat(list2).Distinct().ToArray();
						}
						continue;
					}
					string[] array3 = MaterialKeys(component.m_resources);
					int inferred2 = NearbyBiomeResolver.RecipeBiome(array3.Select(biomeResolver.ResolveProgression).ToArray());
					bool overridden2;
					int num3 = biomeMappings.Apply(text4, inferred2, out overridden2);
					biomeMappings.Add(text4, JournalKind.Building, ((Object)piece).name, Localize(component.m_name), inferred2, num3, overridden2, array3.Select(Localize));
					if (player.IsRecipeKnown(component.m_name))
					{
						List<string> list3 = new List<string> { text3 };
						if (Object.op_Implicit((Object)(object)component.m_craftingStation))
						{
							list3.Add(Localize(component.m_craftingStation.m_name));
						}
						dictionary[text4] = new JournalEntry
						{
							Id = text4,
							KnowledgeKey = component.m_name,
							Kind = JournalKind.Building,
							Name = Localize(component.m_name),
							Icon = component.m_icon,
							Summary = text6,
							MaterialKeys = array3,
							MaterialNames = array3.Select(Localize).ToArray(),
							BiomeMask = num3,
							HasUnresolvedBiome = (num3 == 0),
							Locations = list3.Distinct().ToArray(),
							Category = "Building: " + text5,
							Detail = JournalRules.WithDescription(Localize(component.m_description), "Build with:\n" + text3 + "\n\nCategory:\n" + text5 + "\n\nRequired station:\n" + (Object.op_Implicit((Object)(object)component.m_craftingStation) ? Localize(component.m_craftingStation.m_name) : "None") + "\n\n" + Requirements(component.m_resources))
						};
					}
				}
			}
			biomeMappings.Finish();
			return dictionary.Values.OrderBy<JournalEntry, string>((JournalEntry e) => e.Name, StringComparer.CurrentCultureIgnoreCase).ToList();
		}
	}
	internal sealed class JournalManager
	{
		private static readonly FieldRef<Player, HashSet<string>> Known = AccessTools.FieldRefAccess<Player, HashSet<string>>("m_knownRecipes");

		private static readonly FieldRef<Player, HashSet<string>> KnownMaterials = AccessTools.FieldRefAccess<Player, HashSet<string>>("m_knownMaterial");

		private static readonly MethodInfo Recalculate = AccessTools.Method(typeof(Player), "UpdateKnownRecipesList", (Type[])null, (Type[])null);

		private static readonly MethodInfo UpdatePieces = AccessTools.Method(typeof(Player), "UpdateAvailablePiecesList", (Type[])null, (Type[])null);

		private readonly NearbyBiomeResolver biomeResolver = new NearbyBiomeResolver();

		private readonly RecipeBiomeMappings biomeMappings;

		private readonly JournalPersistence persistence;

		internal bool Dirty = true;

		private bool savePending;

		private readonly Dictionary<string, DiscoverySource> pendingSources = new Dictionary<string, DiscoverySource>();

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

		internal int PendingNotifications;

		internal int PendingStorageNotifications;

		internal int PendingStorageItems;

		internal Sprite PendingStorageIcon;

		internal int Revision;

		internal Player Player { get; private set; }

		internal JournalState State { get; private set; }

		internal List<JournalEntry> Entries { get; private set; } = new List<JournalEntry>();

		internal int UnreadCount { get; private set; }

		internal int UnresolvedNearbyCount => (State?.NearbyDiscoveries?.Count((NearbyDiscoveryRecord r) => r.Active && !r.Promoted && !NearbyBiomeResolver.IsSingle(r.BiomeMask))).GetValueOrDefault();

		internal void ClearNotifications()
		{
			PendingNotifications = (PendingStorageNotifications = (PendingStorageItems = 0));
			PendingStorageIcon = null;
		}

		internal JournalManager(string path)
		{
			persistence = new JournalPersistence(path);
			biomeMappings = new RecipeBiomeMappings(path);
		}

		internal void Attach(Player player)
		{
			if ((Object)(object)player != (Object)(object)Player.m_localPlayer || !Object.op_Implicit((Object)(object)Game.instance))
			{
				return;
			}
			long playerID = Game.instance.GetPlayerProfile().GetPlayerID();
			if ((Object)(object)Player == (Object)(object)player)
			{
				JournalState state = State;
				if (state != null && state.CharacterId == playerID)
				{
					return;
				}
			}
			Save();
			State = null;
			Player = player;
			Entries.Clear();
			pendingSources.Clear();
			ClearNotifications();
			UnreadCount = 0;
			try
			{
				State = persistence.Load(playerID);
			}
			catch (Exception ex)
			{
				Plugin.Instance.LoggerWarning("Journal persistence load failed; preserving original: " + ex.Message);
				persistence.PreserveCorrupt(playerID);
				State = new JournalState
				{
					CharacterId = playerID
				};
			}
			State.Baseline(Known.Invoke(player));
			previousKnowledge = new HashSet<string>(Known.Invoke(player));
			PromoteOwnedNearby(player);
			savePending = true;
			Dirty = true;
			Refresh();
			Plugin.Instance.LoggerInfo("Journal state loaded for character " + playerID);
		}

		internal void Changed(Player player, DiscoverySource source)
		{
			if ((Object)(object)Player != (Object)(object)player || State == null)
			{
				return;
			}
			foreach (string item in Known.Invoke(player))
			{
				if (previousKnowledge.Add(item))
				{
					pendingSources[item] = source;
				}
			}
			previousKnowledge.IntersectWith(Known.Invoke(player));
			Dirty = true;
		}

		internal HashSet<string> BeginNearbyDiscovery()
		{
			if (!Object.op_Implicit((Object)(object)Player))
			{
				return new HashSet<string>();
			}
			return new HashSet<string>(Known.Invoke(Player), StringComparer.Ordinal);
		}

		internal void CompleteNearbyDiscovery(ItemData item, HashSet<string> before)
		{
			if (Object.op_Implicit((Object)(object)Player) && State != null && item?.m_shared != null)
			{
				string[] array = (from key in Known.Invoke(Player)
					where before == null || !before.Contains(key)
					select key).ToArray();
				string itemPrefab = (Object.op_Implicit((Object)(object)item.m_dropPrefab) ? ((Object)item.m_dropPrefab).name : "");
				State.RecordNearby(item.m_shared.m_name, itemPrefab, biomeResolver.Resolve(item.m_shared.m_name), array);
				savePending = true;
				Plugin.Instance.DebugLog("Nearby provenance recorded for " + item.m_shared.m_name + " (" + array.Length + " knowledge keys).");
			}
		}

		internal void PromoteOwnedNearby(Player player)
		{
			if (!((Object)(object)Player != (Object)(object)player) && State != null && ((Humanoid)player).GetInventory() != null)
			{
				IEnumerable<string> materialKeys = from item in ((Humanoid)player).GetInventory().GetAllItems()
					where item?.m_shared != null
					select item.m_shared.m_name;
				if (State.PromoteNearbyMaterials(materialKeys))
				{
					savePending = true;
					Save();
				}
			}
		}

		internal List<NearbyResetSummary> NearbyResetOptions()
		{
			return (from g in (from r in State?.NearbyDiscoveries?.Where((NearbyDiscoveryRecord r) => r.Active && !r.Promoted).ToArray() ?? Array.Empty<NearbyDiscoveryRecord>()
					where NearbyBiomeResolver.IsSingle(r.BiomeMask)
					group r by r.BiomeMask).OrderBy<IGrouping<int, NearbyDiscoveryRecord>, string>((IGrouping<int, NearbyDiscoveryRecord> g) => NearbyBiomeResolver.Name(g.Key), StringComparer.CurrentCultureIgnoreCase)
				select new NearbyResetSummary
				{
					BiomeMask = g.Key,
					Biome = NearbyBiomeResolver.Name(g.Key),
					Materials = g.Select((NearbyDiscoveryRecord r) => r.MaterialKey).Distinct<string>(StringComparer.Ordinal).OrderBy(JournalCatalog.Localize)
						.ToList(),
					KnowledgeKeys = g.SelectMany((NearbyDiscoveryRecord r) => r.KnowledgeKeys).Distinct<string>(StringComparer.Ordinal).ToList(),
					KnowledgeNames = g.SelectMany((NearbyDiscoveryRecord r) => r.KnowledgeKeys).Distinct<string>(StringComparer.Ordinal).SelectMany(delegate(string key)
					{
						string[] array = (from entry in Entries
							where entry.KnowledgeKey == key
							select entry.Name).Distinct().ToArray();
						return (array.Length == 0) ? new string[1] { JournalCatalog.Localize(key) } : array;
					})
						.Distinct()
						.OrderBy<string, string>((string name) => name, StringComparer.CurrentCultureIgnoreCase)
						.ToList()
				}).ToList();
		}

		internal List<NearbyResetSummary> LegacyResetOptions()
		{
			if (!Object.op_Implicit((Object)(object)Player) || State == null)
			{
				return new List<NearbyResetSummary>();
			}
			return (from material in KnownMaterials.Invoke(Player)
				select new
				{
					Material = material,
					Biome = biomeResolver.Resolve(material)
				} into item
				where NearbyBiomeResolver.IsSingle(item.Biome)
				group item by item.Biome into @group
				orderby NearbyBiomeResolver.ProgressionRank(@group.Key) descending
				select @group).ThenBy(group => NearbyBiomeResolver.Name(group.Key), StringComparer.CurrentCultureIgnoreCase).Select(group =>
			{
				HashSet<string> hashSet = new HashSet<string>(group.Select(item => item.Material), StringComparer.Ordinal);
				List<string> list = PreviewAffectedKnowledge(hashSet);
				return new NearbyResetSummary
				{
					BiomeMask = group.Key,
					Biome = NearbyBiomeResolver.Name(group.Key),
					Materials = hashSet.OrderBy(JournalCatalog.Localize).ToList(),
					KnowledgeKeys = list,
					KnowledgeNames = NamesForKnowledge(list)
				};
			}).ToList();
		}

		internal int ResetNearbyBiome(int biomeMask)
		{
			if (!Object.op_Implicit((Object)(object)Player) || State == null || !NearbyBiomeResolver.IsSingle(biomeMask))
			{
				return 0;
			}
			NearbyDiscoveryRecord[] array = State.NearbyDiscoveries.Where((NearbyDiscoveryRecord r) => r.Active && !r.Promoted && r.BiomeMask == biomeMask).ToArray();
			if (array.Length == 0)
			{
				return 0;
			}
			if (Plugin.Instance.LearnStorage.Value)
			{
				Plugin.Instance.SetNearbyDiscovery(enabled: false);
			}
			HashSet<string> hashSet = new HashSet<string>(array.Select((NearbyDiscoveryRecord r) => r.MaterialKey), StringComparer.Ordinal);
			HashSet<string> hashSet2 = new HashSet<string>(array.SelectMany((NearbyDiscoveryRecord r) => r.KnowledgeKeys), StringComparer.Ordinal);
			KnownMaterials.Invoke(Player).ExceptWith(hashSet);
			hashSet2.UnionWith(FindInvalidKnowledge(hashSet));
			Known.Invoke(Player).ExceptWith(hashSet2);
			Plugin.KnowledgeMaintenance = true;
			try
			{
				Recalculate?.Invoke(Player, null);
				UpdatePieces?.Invoke(Player, null);
			}
			finally
			{
				Plugin.KnowledgeMaintenance = false;
			}
			HashSet<string> absent = new HashSet<string>(hashSet2.Where((string key) => !Known.Invoke(Player).Contains(key)), StringComparer.Ordinal);
			State.RemoveEntries(from e in Entries
				where absent.Contains(e.KnowledgeKey)
				select e.Id);
			State.RetireNearby(array);
			previousKnowledge = new HashSet<string>(Known.Invoke(Player), StringComparer.Ordinal);
			pendingSources.Clear();
			savePending = true;
			Dirty = true;
			Refresh();
			Plugin.Instance.DebugLog("Reset " + array.Length + " nearby discoveries for " + NearbyBiomeResolver.Name(biomeMask) + ".");
			return array.Length;
		}

		internal int ResetLegacyBiome(int biomeMask)
		{
			if (!Object.op_Implicit((Object)(object)Player) || State == null || !NearbyBiomeResolver.IsSingle(biomeMask))
			{
				return 0;
			}
			HashSet<string> hashSet = new HashSet<string>(from material in KnownMaterials.Invoke(Player)
				where biomeResolver.Resolve(material) == biomeMask
				select material, StringComparer.Ordinal);
			if (hashSet.Count == 0)
			{
				return 0;
			}
			if (Plugin.Instance.LearnStorage.Value)
			{
				Plugin.Instance.SetNearbyDiscovery(enabled: false);
			}
			KnownMaterials.Invoke(Player).ExceptWith(hashSet);
			HashSet<string> hashSet2 = new HashSet<string>(FindInvalidKnowledge(hashSet), StringComparer.Ordinal);
			hashSet2.UnionWith(hashSet.Where((string material) => Known.Invoke(Player).Contains(material)));
			Known.Invoke(Player).ExceptWith(hashSet2);
			Plugin.KnowledgeMaintenance = true;
			try
			{
				Recalculate?.Invoke(Player, null);
				UpdatePieces?.Invoke(Player, null);
			}
			finally
			{
				Plugin.KnowledgeMaintenance = false;
			}
			HashSet<string> absent = new HashSet<string>(hashSet2.Where((string key) => !Known.Invoke(Player).Contains(key)), StringComparer.Ordinal);
			State.RemoveEntries(from entry in Entries
				where absent.Contains(entry.KnowledgeKey)
				select entry.Id);
			State.RetireNearby(State.NearbyDiscoveries.Where((NearbyDiscoveryRecord record) => record.Active && record.BiomeMask == biomeMask).ToArray());
			previousKnowledge = new HashSet<string>(Known.Invoke(Player), StringComparer.Ordinal);
			pendingSources.Clear();
			savePending = true;
			Dirty = true;
			Refresh();
			Plugin.Instance.DebugLog("Legacy reset removed " + hashSet.Count + " known materials for " + NearbyBiomeResolver.Name(biomeMask) + ".");
			return hashSet.Count;
		}

		private List<string> PreviewAffectedKnowledge(HashSet<string> materials)
		{
			HashSet<string> hashSet = new HashSet<string>(materials.Where((string material) => Known.Invoke(Player).Contains(material)), StringComparer.Ordinal);
			foreach (Recipe recipe in ObjectDB.instance.m_recipes)
			{
				if (Object.op_Implicit((Object)(object)recipe) && Object.op_Implicit((Object)(object)recipe.m_item) && recipe.m_item.m_itemData?.m_shared != null && (recipe.m_resources ?? Array.Empty<Requirement>()).Any((Requirement requirement) => Object.op_Implicit((Object)(object)requirement?.m_resItem) && requirement.m_amount > 0 && materials.Contains(requirement.m_resItem.m_itemData.m_shared.m_name)))
				{
					hashSet.Add(recipe.m_item.m_itemData.m_shared.m_name);
				}
			}
			foreach (Piece item in AllPieces())
			{
				if ((item.m_resources ?? Array.Empty<Requirement>()).Any((Requirement requirement) => Object.op_Implicit((Object)(object)requirement?.m_resItem) && requirement.m_amount > 0 && materials.Contains(requirement.m_resItem.m_itemData.m_shared.m_name)))
				{
					hashSet.Add(item.m_name);
				}
			}
			hashSet.IntersectWith(Known.Invoke(Player));
			return hashSet.OrderBy<string, string>((string key) => key, StringComparer.Ordinal).ToList();
		}

		private List<string> NamesForKnowledge(IEnumerable<string> keys)
		{
			return keys.SelectMany(delegate(string key)
			{
				string[] array = (from entry in Entries
					where entry.KnowledgeKey == key
					select entry.Name).Distinct().ToArray();
				return (array.Length == 0) ? new string[1] { JournalCatalog.Localize(key) } : array;
			}).Distinct().OrderBy<string, string>((string name) => name, StringComparer.CurrentCultureIgnoreCase)
				.ToList();
		}

		private IEnumerable<string> FindInvalidKnowledge(HashSet<string> removedMaterials)
		{
			HashSet<string> hashSet = new HashSet<string>(StringComparer.Ordinal);
			foreach (IGrouping<string, Recipe> item in ObjectDB.instance.m_recipes.Where((Recipe recipe) => Object.op_Implicit((Object)(object)recipe) && Object.op_Implicit((Object)(object)recipe.m_item) && recipe.m_item.m_itemData?.m_shared != null).ToArray().GroupBy<Recipe, string>((Recipe recipe) => recipe.m_item.m_itemData.m_shared.m_name, StringComparer.Ordinal))
			{
				if (Known.Invoke(Player).Contains(item.Key) && item.Any((Recipe recipe) => (recipe.m_resources ?? Array.Empty<Requirement>()).Any((Requirement requirement) => Object.op_Implicit((Object)(object)requirement?.m_resItem) && requirement.m_amount > 0 && removedMaterials.Contains(requirement.m_resItem.m_itemData.m_shared.m_name))) && !item.Any((Recipe recipe) => Player.HaveRequirements(recipe, true, 0, 1)))
				{
					hashSet.Add(item.Key);
				}
			}
			foreach (IGrouping<string, Piece> item2 in AllPieces().GroupBy<Piece, string>((Piece piece) => piece.m_name, StringComparer.Ordinal))
			{
				if (Known.Invoke(Player).Contains(item2.Key) && item2.Any((Piece piece) => (piece.m_resources ?? Array.Empty<Requirement>()).Any((Requirement requirement) => Object.op_Implicit((Object)(object)requirement?.m_resItem) && requirement.m_amount > 0 && removedMaterials.Contains(requirement.m_resItem.m_itemData.m_shared.m_name))) && !item2.Any((Piece piece) => Player.HaveRequirements(piece, (RequirementMode)1)))
				{
					hashSet.Add(item2.Key);
				}
			}
			return hashSet;
		}

		private IEnumerable<Piece> AllPieces()
		{
			return from prefab in (from item in (from item in ObjectDB.instance.m_items
						where Object.op_Implicit((Object)(object)item)
						select item.GetComponent<ItemDrop>() into drop
						where Object.op_Implicit((Object)(object)drop)
						select drop.m_itemData).Concat(((Humanoid)Player).GetInventory().GetAllItems())
					where Object.op_Implicit((Object)(object)item?.m_shared?.m_buildPieces)
					select item.m_shared.m_buildPieces).Distinct().SelectMany((PieceTable table) => table.m_pieces)
				where Object.op_Implicit((Object)(object)prefab)
				select prefab.GetComponent<Piece>() into piece
				where Object.op_Implicit((Object)(object)piece)
				select piece;
		}

		internal void Refresh()
		{
			if (!Object.op_Implicit((Object)(object)Player) || State == null || !Object.op_Implicit((Object)(object)ObjectDB.instance) || ObjectDB.instance.m_recipes.Count == 0)
			{
				return;
			}
			List<JournalEntry> list = JournalCatalog.Read(Player, biomeResolver, biomeMappings);
			foreach (JournalEntry item in list)
			{
				DiscoverySource value;
				DiscoverySource discoverySource = (pendingSources.TryGetValue(item.KnowledgeKey, out value) ? value : DiscoverySource.Vanilla);
				bool added;
				EntryRecord entryRecord = State.Observe(item.Id, item.KnowledgeKey, discoverySource, out added);
				if (!added)
				{
					continue;
				}
				savePending = true;
				if (!entryRecord.Read)
				{
					PendingNotifications++;
					if (discoverySource == DiscoverySource.NearbyStorage)
					{
						PendingStorageNotifications++;
						if (!Object.op_Implicit((Object)(object)PendingStorageIcon))
						{
							PendingStorageIcon = item.Icon;
						}
					}
				}
				Plugin.Instance.DebugLog("Journal entry detected: " + item.Id + " (" + discoverySource.ToString() + ")");
			}
			Entries = list;
			UnreadCount = Entries.Count((JournalEntry e) => State.IsUnread(e.Id));
			Dirty = false;
			Revision++;
			Save();
		}

		internal void Read(IEnumerable<string> ids)
		{
			if (State != null && State.MarkRead(ids))
			{
				UnreadCount = Entries.Count((JournalEntry e) => State.IsUnread(e.Id));
				savePending = true;
				Revision++;
				Save();
			}
		}

		internal void Save()
		{
			if (State == null || !savePending)
			{
				return;
			}
			try
			{
				persistence.Save(State);
				savePending = false;
			}
			catch (Exception ex)
			{
				Plugin.Instance.LoggerWarning("Journal persistence save failed: " + ex.Message);
			}
		}

		internal void Detach()
		{
			Save();
			Player = null;
			State = null;
			Entries.Clear();
			pendingSources.Clear();
			ClearNotifications();
			UnreadCount = 0;
			Revision++;
		}
	}
	internal sealed class NearbyResetSummary
	{
		internal int BiomeMask;

		internal string Biome;

		internal List<string> Materials;

		internal List<string> KnowledgeKeys;

		internal List<string> KnowledgeNames;
	}
	public sealed class JournalScrollRect : ScrollRect
	{
		private int lastWheelFrame = -1;

		public override void OnScroll(PointerEventData eventData)
		{
			//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)
			if (((UIBehaviour)this).IsActive())
			{
				Vector2 scrollDelta = eventData.scrollDelta;
				if (((Vector2)(ref scrollDelta)).sqrMagnitude != 0f)
				{
					lastWheelFrame = Time.frameCount;
					((ScrollRect)this).OnScroll(eventData);
				}
			}
		}

		protected override void LateUpdate()
		{
			//IL_0046: 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_0062: 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_0090: Unknown result type (might be due to invalid IL or missing references)
			//IL_0095: Unknown result type (might be due to invalid IL or missing references)
			((ScrollRect)this).LateUpdate();
			if (!((UIBehaviour)this).IsActive() || !Object.op_Implicit((Object)(object)((ScrollRect)this).content) || !Object.op_Implicit((Object)(object)((ScrollRect)this).viewport) || lastWheelFrame == Time.frameCount)
			{
				return;
			}
			Canvas componentInParent = ((Component)this).GetComponentInParent<Canvas>();
			Camera val = ((Object.op_Implicit((Object)(object)componentInParent) && (int)componentInParent.renderMode != 0) ? componentInParent.worldCamera : null);
			if (RectTransformUtility.RectangleContainsScreenPoint(((ScrollRect)this).viewport, Vector2.op_Implicit(ZInput.pointerPosition), val))
			{
				float mouseScrollWheel = ZInput.GetMouseScrollWheel();
				Rect rect = ((ScrollRect)this).content.rect;
				float height = ((Rect)(ref rect)).height;
				rect = ((ScrollRect)this).viewport.rect;
				float num = height - ((Rect)(ref rect)).height;
				if (!Mathf.Approximately(mouseScrollWheel, 0f) && !(num <= 0f))
				{
					((ScrollRect)this).StopMovement();
					((ScrollRect)this).verticalNormalizedPosition = Mathf.Clamp01(((ScrollRect)this).verticalNormalizedPosition + Mathf.Sign(mouseScrollWheel) * 64f / num);
					lastWheelFrame = Time.frameCount;
				}
			}
		}
	}
	public sealed class JournalSearchDelay : MonoBehaviour
	{
		private readonly JournalSearchDebounce debounce = new JournalSearchDebounce();

		internal Action Elapsed;

		internal void Schedule()
		{
			debounce.Schedule(Time.unscaledTime);
		}

		internal void Cancel()
		{
			debounce.Cancel();
		}

		private void OnDisable()
		{
			Cancel();
		}

		private void Update()
		{
			if (debounce.TryConsume(Time.unscaledTime) && Object.op_Implicit((Object)(object)Plugin.Instance))
			{
				Plugin.Instance.Safe(delegate
				{
					Elapsed?.Invoke();
				});
			}
		}
	}
	internal sealed class JournalSkin
	{
		internal readonly Image Panel;

		internal readonly Image Inset;

		internal readonly Image Tab;

		internal readonly Image TabBorder;

		internal readonly Image RepairBacking;

		internal readonly Image RepairButton;

		internal readonly TMP_Text Body;

		internal readonly TMP_Text Title;

		internal readonly TMP_Text RowTitle;

		internal readonly TMP_Text ButtonText;

		internal readonly TMP_Text ItemTitle;

		internal readonly Color Selected;

		internal readonly float ScrollSensitivity;

		internal JournalSkin(InventoryGui gui)
		{
			//IL_012c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0124: Unknown result type (might be due to invalid IL or missing references)
			//IL_0131: Unknown result type (might be due to invalid IL or missing references)
			Panel = FindImage((Transform)(object)gui.m_crafting, "Bkg");
			Inset = FindImage((Transform)(object)gui.m_crafting, "Decription");
			Tab = ((Component)gui.m_tabCraft).GetComponent<Image>();
			TabBorder = FindImage(((Component)gui.m_tabCraft).transform.parent, "TabBorder");
			RepairBacking = ((Component)gui.m_repairPanel).GetComponent<Image>();
			RepairButton = ((Component)gui.m_repairButton).GetComponent<Image>();
			Body = gui.m_recipeDecription;
			Title = gui.m_craftingStationName;
			ItemTitle = gui.m_recipeName;
			Transform obj = gui.m_recipeElementPrefab.transform.Find("name");
			RowTitle = ((obj != null) ? ((Component)obj).GetComponent<TMP_Text>() : null) ?? Body;
			ButtonText = ((Component)gui.m_tabCraft).GetComponentInChildren<TMP_Text>(true) ?? Body;
			Image val = FindImage(gui.m_recipeElementPrefab.transform, "selected");
			Selected = (Color)(Object.op_Implicit((Object)(object)val) ? ((Graphic)val).color : new Color(0.76f, 0.5f, 0.18f));
			ScrollRect componentInParent = ((Component)gui.m_recipeListRoot).GetComponentInParent<ScrollRect>();
			ScrollSensitivity = (Object.op_Implicit((Object)(object)componentInParent) ? componentInParent.scrollSensitivity : 3000f);
		}

		private static Image FindImage(Transform root, string path)
		{
			if (!Object.op_Implicit((Object)(object)root))
			{
				return null;
			}
			Transform obj = root.Find(path);
			if (obj == null)
			{
				return null;
			}
			return ((Component)obj).GetComponent<Image>();
		}

		internal static void CopyImage(Image target, Image source)
		{
			//IL_0023: Unknown result type (might be due to invalid IL or missing references)
			//IL_002f: Unknown result type (might be due to invalid IL or missing references)
			if (Object.op_Implicit((Object)(object)source))
			{
				target.sprite = source.sprite;
				((Graphic)target).material = ((Graphic)source).material;
				target.type = source.type;
				((Graphic)target).color = ((Graphic)source).color;
				target.pixelsPerUnitMultiplier = source.pixelsPerUnitMultiplier;
				target.preserveAspect = source.preserveAspect;
				target.fillCenter = source.fillCenter;
			}
		}

		internal static void CopyText(TMP_Text target, TMP_Text source)
		{
			//IL_0023: Unknown result type (might be due to invalid IL or missing references)
			//IL_002f: Unknown result type (might be due to invalid IL or missing references)
			if (Object.op_Implicit((Object)(object)source))
			{
				target.font = source.font;
				target.fontSharedMaterial = source.fontSharedMaterial;
				target.fontStyle = source.fontStyle;
				((Graphic)target).color = ((Graphic)source).color;
				target.characterSpacing = source.characterSpacing;
				target.wordSpacing = source.wordSpacing;
			}
		}
	}
	public enum DiscoverySource
	{
		Vanilla,
		NearbyStorage
	}
	public enum JournalKind
	{
		Crafting,
		Building
	}
	public enum JournalFilter
	{
		New,
		All,
		Crafting,
		Building
	}
	public sealed class EntryRecord
	{
		public string Id;

		public bool Read;

		public DiscoverySource Source;

		public string DiscoveredUtc;
	}
	public sealed class NearbyDiscoveryRecord
	{
		public string MaterialKey;

		public string ItemPrefab;

		public int BiomeMask;

		public string LearnedUtc;

		public bool Active = true;

		public bool Promoted;

		public List<string> KnowledgeKeys = new List<string>();
	}
	public sealed class JournalState
	{
		public int Version = 2;

		public long CharacterId;

		public bool Initialized;

		public List<string> BaselineKnowledge = new List<string>();

		public List<EntryRecord> Entries = new List<EntryRecord>();

		public List<NearbyDiscoveryRecord> NearbyDiscoveries = new List<NearbyDiscoveryRecord>();

		private Dictionary<string, EntryRecord> index;

		private HashSet<string> baselineIndex;

		private void EnsureIndex()
		{
			if (index == null || index.Count != Entries.Count)
			{
				index = Entries.ToDictionary<EntryRecord, string>((EntryRecord e) => e.Id, StringComparer.Ordinal);
			}
			if (baselineIndex == null)
			{
				baselineIndex = new HashSet<string>(BaselineKnowledge, StringComparer.Ordinal);
			}
		}

		public EntryRecord Find(string id)
		{
			EnsureIndex();
			if (!index.TryGetValue(id, out var value))
			{
				return null;
			}
			return value;
		}

		public void Baseline(IEnumerable<string> knowledge)
		{
			if (!Initialized)
			{
				BaselineKnowledge = knowledge.Distinct<string>(StringComparer.Ordinal).ToList();
				baselineIndex = new HashSet<string>(BaselineKnowledge, StringComparer.Ordinal);
				Initialized = true;
			}
		}

		public EntryRecord Observe(string id, string vanillaKey, DiscoverySource source, out bool added)
		{
			if (!Initialized)
			{
				throw new InvalidOperationException("Baseline must precede observation.");
			}
			EntryRecord entryRecord = Find(id);
			added = entryRecord == null;
			if (entryRecord != null)
			{
				return entryRecord;
			}
			entryRecord = new EntryRecord
			{
				Id = id,
				Read = baselineIndex.Contains(vanillaKey),
				Source = source,
				DiscoveredUtc = DateTime.UtcNow.ToString("O")
			};
			Entries.Add(entryRecord);
			index.Add(id, entryRecord);
			return entryRecord;
		}

		public bool IsUnread(string id)
		{
			EntryRecord entryRecord = Find(id);
			if (entryRecord == null)
			{
				return false;
			}
			return !entryRecord.Read;
		}

		public bool MarkRead(IEnumerable<string> ids)
		{
			EnsureIndex();
			bool result = false;
			foreach (string id in ids)
			{
				if (index.TryGetValue(id, out var value) && !value.Read)
				{
					value.Read = true;
					result = true;
				}
			}
			return result;
		}

		public void RecordNearby(string materialKey, string itemPrefab, int biomeMask, IEnumerable<string> knowledgeKeys)
		{
			if (!string.IsNullOrEmpty(materialKey))
			{
				NearbyDiscoveries.Add(new NearbyDiscoveryRecord
				{
					MaterialKey = materialKey,
					ItemPrefab = (itemPrefab ?? ""),
					BiomeMask = biomeMask,
					LearnedUtc = DateTime.UtcNow.ToString("O"),
					KnowledgeKeys = (knowledgeKeys ?? Enumerable.Empty<string>()).Distinct<string>(StringComparer.Ordinal).ToList()
				});
			}
		}

		public bool PromoteNearbyMaterials(IEnumerable<string> materialKeys)
		{
			HashSet<string> hashSet = new HashSet<string>(materialKeys ?? Enumerable.Empty<string>(), StringComparer.Ordinal);
			bool result = false;
			foreach (NearbyDiscoveryRecord nearbyDiscovery in NearbyDiscoveries)
			{
				if (nearbyDiscovery.Active && !nearbyDiscovery.Promoted && hashSet.Contains(nearbyDiscovery.MaterialKey))
				{
					nearbyDiscovery.Promoted = true;
					result = true;
				}
			}
			return result;
		}

		public bool RetireNearby(IEnumerable<NearbyDiscoveryRecord> records)
		{
			bool result = false;
			foreach (NearbyDiscoveryRecord item in records ?? Enumerable.Empty<NearbyDiscoveryRecord>())
			{
				if (item != null && item.Active)
				{
					item.Active = false;
					result = true;
				}
			}
			return result;
		}

		public bool RemoveEntries(IEnumerable<string> ids)
		{
			HashSet<string> remove = new HashSet<string>(ids ?? Enumerable.Empty<string>(), StringComparer.Ordinal);
			int num = Entries.RemoveAll((EntryRecord e) => remove.Contains(e.Id));
			if (num > 0)
			{
				index = null;
			}
			return num > 0;
		}
	}
	public sealed class JournalSelection
	{
		public string Id { get; private set; }

		public string Select(string id)
		{
			if (Id == id)
			{
				return null;
			}
			string id2 = Id;
			Id = id;
			return id2;
		}

		public string Leave()
		{
			return Select(null);
		}
	}
	public sealed class JournalSearchDebounce
	{
		private double? due;

		public void Schedule(double now)
		{
			due = now + 0.25;
		}

		public void Cancel()
		{
			due = null;
		}

		public bool TryConsume(double now)
		{
			if (!due.HasValue || now < due.Value)
			{
				return false;
			}
			due = null;
			return true;
		}
	}
	public readonly struct JournalWindow
	{
		public readonly int First;

		public readonly int Count;

		public const int RowHeight = 64;

		public JournalWindow(int total, float offset, float viewportHeight)
		{
			int val = Math.Max(0, (int)Math.Ceiling(viewportHeight / 64f)) + 2;
			Count = Math.Min(Math.Max(0, total), val);
			First = Math.Max(0, Math.Min(total - Count, (int)Math.Floor(Math.Max(0f, offset) / 64f) - 1));
		}
	}
	public static class JournalRules
	{
		public static string StableId(JournalKind kind, params string[] parts)
		{
			return string.Concat(kind.ToString(), ":", string.Concat(parts.Select((string p) => (p ?? "").Length + ":" + p)));
		}

		public static bool Matches(JournalKind kind, bool unread, JournalFilter filter, string search, string title)
		{
			if ((filter == JournalFilter.New && !unread) || (filter == JournalFilter.Crafting && kind != JournalKind.Crafting) || (filter == JournalFilter.Building && kind != JournalKind.Building))
			{
				return false;
			}
			if (!string.IsNullOrWhiteSpace(search))
			{
				return (title ?? "").IndexOf(search.Trim(), StringComparison.CurrentCultureIgnoreCase) >= 0;
			}
			return true;
		}

		public static bool MatchesDetails(int biomeMask, bool unresolvedBiome, IEnumerable<string> materialNames, IEnumerable<string> locations, string category, int selectedBiome, string selectedMaterial, string selectedLocation, string selectedCategory)
		{
			if (selectedBiome == -1 && !unresolvedBiome)
			{
				return false;
			}
			if (selectedBiome > 0 && (biomeMask & selectedBiome) == 0)
			{
				return false;
			}
			if (!string.IsNullOrWhiteSpace(selectedMaterial) && !(materialNames ?? Enumerable.Empty<string>()).Any((string name) => (name ?? "").IndexOf(selectedMaterial.Trim(), StringComparison.CurrentCultureIgnoreCase) >= 0))
			{
				return false;
			}
			if (!string.IsNullOrEmpty(selectedLocation) && !(locations ?? Enumerable.Empty<string>()).Contains<string>(selectedLocation, StringComparer.CurrentCultureIgnoreCase))
			{
				return false;
			}
			if (!string.IsNullOrEmpty(selectedCategory))
			{
				return string.Equals(category, selectedCategory, StringComparison.CurrentCultureIgnoreCase);
			}
			return true;
		}

		public static int ProgressionBiome(IEnumerable<int> materialBiomeMasks, IEnumerable<int> progressionOrder, bool requireAny = false)
		{
			int[] array = (progressionOrder ?? Enumerable.Empty<int>()).Where((int value) => value > 0 && (value & (value - 1)) == 0).Distinct().ToArray();
			int result = 0;
			int num = (requireAny ? int.MaxValue : (-1));
			foreach (int item in materialBiomeMasks ?? Enumerable.Empty<int>())
			{
				int num2 = -1;
				for (int num3 = 0; num3 < array.Length; num3++)
				{
					if ((item & array[num3]) != 0)
					{
						num2 = num3;
						break;
					}
				}
				if (num2 >= 0 && (requireAny ? (num2 < num) : (num2 > num)))
				{
					result = array[num2];
					num = num2;
				}
			}
			return result;
		}

		public static string WithDescription(string localizedDescription, string details)
		{
			string text = (localizedDescription ?? "").Trim();
			if (!string.IsNullOrEmpty(text))
			{
				return text + "\n\n" + details;
			}
			return details ?? "";
		}

		public static string InspectionHeading(int quality, bool upgradeable)
		{
			return "Item stats · Quality " + Math.Max(1, quality) + (upgradeable ? " (base crafted item; upgrades differ)" : " (crafted item)");
		}

		public static float PanelHeight(float availableHeight)
		{
			if (float.IsNaN(availableHeight) || float.IsInfinity(availableHeight))
			{
				return 660f;
			}
			return Math.Max(660f, Math.Min(780f, availableHeight - 120f));
		}

		public static bool Eligible(bool valid, bool playerBuilt, float distanceSquared, float radius, bool access)
		{
			return valid && playerBuilt && radius > 0f && distanceSquared <= radius * radius && access;
		}
	}
	public sealed class JournalPersistence
	{
		private static readonly XmlSerializer Serializer = new XmlSerializer(typeof(JournalState));

		private readonly string directory;

		public JournalPersistence(string directory)
		{
			this.directory = directory;
		}

		public string PathFor(long id)
		{
			return Path.Combine(directory, id.ToString(CultureInfo.InvariantCulture) + ".xml");
		}

		public JournalState Load(long id)
		{
			string path = PathFor(id);
			if (!File.Exists(path))
			{
				return new JournalState
				{
					CharacterId = id
				};
			}
			using FileStream stream = File.OpenRead(path);
			JournalState journalState = (JournalState)Serializer.Deserialize(stream);
			if ((journalState.Version != 1 && journalState.Version != 2) || journalState.CharacterId != id || journalState.Entries == null || journalState.BaselineKnowledge == null || journalState.Entries.Any((EntryRecord e) => e == null || string.IsNullOrEmpty(e.Id)) || journalState.Entries.Select((EntryRecord e) => e.Id).Distinct<string>(StringComparer.Ordinal).Count() != journalState.Entries.Count)
			{
				throw new InvalidDataException("Invalid journal state or character identifier.");
			}
			if (journalState.NearbyDiscoveries == null)
			{
				journalState.NearbyDiscoveries = new List<NearbyDiscoveryRecord>();
			}
			if (journalState.NearbyDiscoveries.Any((NearbyDiscoveryRecord r) => r == null || string.IsNullOrEmpty(r.MaterialKey)))
			{
				throw new InvalidDataException("Invalid nearby discovery history.");
			}
			foreach (NearbyDiscoveryRecord nearbyDiscovery in journalState.NearbyDiscoveries)
			{
				if (nearbyDiscovery.KnowledgeKeys == null)
				{
					nearbyDiscovery.KnowledgeKeys = new List<string>();
				}
			}
			journalState.Version = 2;
			return journalState;
		}

		public void Save(JournalState state)
		{
			Directory.CreateDirectory(directory);
			string text = PathFor(state.CharacterId);
			string text2 = text + ".tmp";
			using (FileStream stream = File.Create(text2))
			{
				Serializer.Serialize(stream, state);
			}
			if (File.Exists(text))
			{
				File.Replace(text2, text, text + ".bak");
			}
			else
			{
				File.Move(text2, text);
			}
		}

		public void PreserveCorrupt(long id)
		{
			string text = PathFor(id);
			if (File.Exists(text))
			{
				File.Copy(text, text + ".corrupt-" + DateTime.UtcNow.Ticks);
			}
		}
	}
	internal sealed class JournalUI
	{
		private sealed class RowView
		{
			internal Button Button;

			internal TMP_Text Title;

			internal TMP_Text Summary;

			internal Image Icon;

			internal GameObject Badge;

			internal JournalEntry Entry;
		}

		private sealed class FilterChoice
		{
			internal string Key;

			internal string Label;
		}

		private InventoryGui owner;

		private GameObject panel;

		private GameObject launcher;

		private GameObject launcherBacking;

		private RectTransform rows;

		private readonly List<RowView> rowPool = new List<RowView>();

		private List<JournalEntry> matches = new List<JournalEntry>();

		private TMP_Text emptyMessage;

		private int firstBound = -1;

		private bool updatingList;

		private bool ready;

		private TMP_Text launcherText;

		private TMP_Text detail;

		private TMP_Text detailTitle;

		private Image detailIcon;

		private Button inspectButton;

		private TMP_Text inspectButtonText;

		private bool inspecting;

		private TMP_InputField search;

		private JournalSearchDelay searchDelay;

		private string appliedSearch = "";

		private JournalSkin skin;

		private ScrollRect listScroll;

		private ScrollRect detailScroll;

		private readonly Dictionary<JournalFilter, Button> filters = new Dictionary<JournalFilter, Button>();

		private Button nearbyDiscoveryButton;

		private TMP_Text nearbyDiscoveryText;

		private GameObject resetPanel;

		private TMP_Text resetBiomeText;

		private TMP_Text resetDetails;

		private TMP_Text resetActionText;

		private TMP_Text resetIntro;

		private Button resetAction;

		private Button resetNearbyTab;

		private Button resetBiomeTab;

		private List<NearbyResetSummary> resetOptions = new List<NearbyResetSummary>();

		private int resetIndex;

		private bool resetArmed;

		private bool resetWholeBiome;

		private GameObject filterPanel;

		private Button filtersButton;

		private TMP_Text filtersButtonText;

		private TMP_Text filterBiomeText;

		private TMP_Text filterLocationText;

		private TMP_Text filterCategoryText;

		private TMP_InputField filterMaterialInput;

		private JournalSearchDelay filterMaterialDelay;

		private List<FilterChoice> biomeChoices = new List<FilterChoice>();

		private List<FilterChoice> locationChoices = new List<FilterChoice>();

		private List<FilterChoice> categoryChoices = new List<FilterChoice>();

		private string selectedBiome = "";

		private string materialQuery = "";

		private string selectedLocation = "";

		private string selectedCategory = "";

		private long filterCharacterId;

		private bool displayedNearbyDiscovery;

		private float displayedRadius = -1f;

		private JournalFilter filter;

		private int revision = -1;

		private readonly JournalSelection selection = new JournalSelection();

		private readonly List<CanvasGroup> inputGuards = new List<CanvasGroup>();

		private string selectedId => selection.Id;

		internal bool IsOpen
		{
			get
			{
				if (Object.op_Implicit((Object)(object)panel))
				{
					return panel.activeSelf;
				}
				return false;
			}
		}

		internal bool SearchFocused
		{
			get
			{
				if (Object.op_Implicit((Object)(object)panel) && panel.activeSelf)
				{
					if (!Object.op_Implicit((Object)(object)search) || !search.isFocused)
					{
						if (Object.op_Implicit((Object)(object)filterMaterialInput))
						{
							return filterMaterialInput.isFocused;
						}
						return false;
					}
					return true;
				}
				return false;
			}
		}

		private JournalManager Journal => Plugin.Instance.Journal;

		internal void Ensure(InventoryGui gui)
		{
			//IL_005e: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bc: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c2: Expected O, but got Unknown
			//IL_00de: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e3: Unknown result type (might be due to invalid IL or missing references)
			//IL_010d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0114: Unknown result type (might be due to invalid IL or missing references)
			//IL_0126: Unknown result type (might be due to invalid IL or missing references)
			//IL_0137: Unknown result type (might be due to invalid IL or missing references)
			//IL_0142: Unknown result type (might be due to invalid IL or missing references)
			//IL_0191: Unknown result type (might be due to invalid IL or missing references)
			//IL_019c: Expected O, but got Unknown
			//IL_01c9: Unknown result type (might be due to invalid IL or missing references)
			//IL_01d4: Expected O, but got Unknown
			//IL_0273: Unknown result type (might be due to invalid IL or missing references)
			//IL_027e: Expected O, but got Unknown
			//IL_02ec: Unknown result type (might be due to invalid IL or missing references)
			//IL_02f7: Expected O, but got Unknown
			//IL_0324: Unknown result type (might be due to invalid IL or missing references)
			//IL_032f: Expected O, but got Unknown
			//IL_040c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0417: Expected O, but got Unknown
			//IL_0489: Unknown result type (might be due to invalid IL or missing references)
			//IL_04f7: Unknown result type (might be due to invalid IL or missing references)
			//IL_05b0: Unknown result type (might be due to invalid IL or missing references)
			//IL_06d4: Unknown result type (might be due to invalid IL or missing references)
			//IL_077e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0789: Expected O, but got Unknown
			if (ready && (Object)(object)owner == (Object)(object)gui && Object.op_Implicit((Object)(object)launcher) && Object.op_Implicit((Object)(object)panel))
			{
				return;
			}
			Dispose();
			owner = gui;
			skin = new JournalSkin(gui);
			CreateLauncher(gui);
			panel = ((Component)Box(gui.m_inventoryRoot, "DiscoveryJournal", Color.white, skin.Panel)).gameObject;
			panel.SetActive(false);
			searchDelay = panel.AddComponent<JournalSearchDelay>();
			searchDelay.Elapsed = delegate
			{
				Render(resetScroll: true);
			};
			RectTransform val = (RectTransform)panel.transform;
			Transform inventoryRoot = gui.m_inventoryRoot;
			RectTransform val2 = (RectTransform)(object)((inventoryRoot is RectTransform) ? inventoryRoot : null);
			float availableHeight;
			if (!Object.op_Implicit((Object)(object)val2))
			{
				availableHeight = 0f;
			}
			else
			{
				Rect rect = val2.rect;
				availableHeight = ((Rect)(ref rect)).height;
			}
			float num = JournalRules.PanelHeight(availableHeight);
			float height = num - 244f;
			Vector2 val3 = default(Vector2);
			((Vector2)(ref val3))..ctor(0.5f, 0.5f);
			val.anchorMax = val3;
			val.anchorMin = val3;
			val.pivot = new Vector2(0.5f, 0.5f);
			val.sizeDelta = new Vector2(1000f, num);
			val.anchoredPosition = Vector2.zero;
			Place(Label((Transform)(object)val, "Discovery Journal", 30, skin.Title).rectTransform, 24f, 14f, 550f, 42f);
			Place(((Component)Button((Transform)(object)val, "Back to Inventory", new UnityAction(Close))).GetComponent<RectTransform>(), 756f, 18f, 220f, 36f);
			nearbyDiscoveryButton = Button((Transform)(object)val, "Nearby Discovery: OFF", new UnityAction(ToggleNearbyDiscovery));
			Place(((Component)nearbyDiscoveryButton).GetComponent<RectTransform>(), 24f, 62f, 250f, 36f);
			nearbyDiscoveryText = ((Component)nearbyDiscoveryButton).GetComponentInChildren<TMP_Text>();
			nearbyDiscoveryText.fontSize = 16f;
			UITooltip obj = ((Component)nearbyDiscoveryButton).gameObject.AddComponent<UITooltip>();
			obj.m_topic = "Nearby Storage Discovery";
			obj.m_text = "When enabled, accessible player-built storage within the configured radius can teach this character about items. Changes are saved to the BepInEx config.";
			obj.m_tooltipPrefab = ((Component)owner.m_repairButton).GetComponent<UITooltip>()?.m_tooltipPrefab;
			Button obj2 = Button((Transform)(object)val, "Reset Recipes", new UnityAction(OpenNearbyReset));
			Place(((Component)obj2).GetComponent<RectTransform>(), 284f, 62f, 170f, 36f);
			UITooltip obj3 = ((Component)obj2).gameObject.AddComponent<UITooltip>();
			obj3.m_topic = "Reset Recipes";
			obj3.m_text = "Opens a review window.\nChoose nearby-only or whole-biome reset.\nNothing is deleted by this button.";
			obj3.m_tooltipPrefab = ((Component)owner.m_repairButton).GetComponent<UITooltip>()?.m_tooltipPrefab;
			Place(((Component)Button((Transform)(object)val, "Mark All Read", (UnityAction)delegate
			{
				Plugin.Instance.Safe(delegate
				{
					Journal.Read(Journal.Entries.Select((JournalEntry e) => e.Id));
					Render(resetScroll: true);
				});
			})).GetComponent<RectTransform>(), 464f, 62f, 170f, 36f);
			filtersButton = Button((Transform)(object)val, "Filters", new UnityAction(OpenFilters));
			filtersButtonText = ((Component)filtersButton).GetComponentInChildren<TMP_Text>();
			Place(((Component)filtersButton).GetComponent<RectTransform>(), 806f, 62f, 170f, 36f);
			UITooltip obj4 = ((Component)filtersButton).gameObject.AddComponent<UITooltip>();
			obj4.m_topic = "Recipe Filters";
			obj4.m_text = "Filter known entries by biome,\nrequired material, crafting location,\nor item/building category.";
			obj4.m_tooltipPrefab = ((Component)owner.m_repairButton).GetComponent<UITooltip>()?.m_tooltipPrefab;
			int num2 = 24;
			foreach (JournalFilter value in Enum.GetValues(typeof(JournalFilter)))
			{
				JournalFilter captured = value;
				Button val4 = Button((Transform)(object)val, value.ToString(), (UnityAction)delegate
				{
					filter = captured;
					Render(resetScroll: true);
				});
				filters[value] = val4;
				Place(((Component)val4).GetComponent<RectTransform>(), num2, 112f, 120f, 36f);
				num2 += 128;
			}
			RectTransform obj5 = Box((Transform)(object)val, "TabBorder", new Color(0.8f, 0.55f, 0.12f), skin.TabBorder);
			Place(obj5, 12f, 147f, 976f, 2f);
			((Graphic)((Component)obj5).GetComponent<Image>()).raycastTarget = false;
			Place(Label((Transform)(object)val, "Search:", 18).rectTransform, 24f, 164f, 75f, 36f);
			RectTransform val5 = Box((Transform)(object)val, "Search", Color.white, skin.Inset);
			Place(val5, 100f, 162f, 876f, 38f);
			search = ((Component)val5).gameObject.AddComponent<TMP_InputField>();
			TMP_Text val6 = Label((Transform)(object)val5, "", 18);
			Stretch(val6.rectTransform, 10f, 5f, 10f, 5f);
			TMP_Text val7 = Label((Transform)(object)val5, "Recipe or build-piece name", 18);
			Stretch(val7.rectTransform, 10f, 5f, 10f, 5f);
			((Graphic)val7).color = new Color(0.6f, 0.6f, 0.55f);
			search.textViewport = val5;
			search.textComponent = val6;
			search.placeholder = (Graphic)(object)val7;
			search.lineType = (LineType)0;
			((UnityEvent<string>)(object)search.onValueChanged).AddListener((UnityAction<string>)delegate
			{
				searchDelay.Schedule();
			});
			rows = Scroll((Transform)(object)val, "KnownEntries", 24f, 218f, 472f, height);
			listScroll = ((Component)((Transform)rows).parent).GetComponent<ScrollRect>();
			emptyMessage = Label((Transform)(object)rows, "No matching known entries.", 18);
			Place(emptyMessage.rectTransform, 12f, 12f, 420f, 50f);
			((UnityEvent<Vector2>)(object)listScroll.onValueChanged).AddListener((UnityAction<Vector2>)delegate
			{
				Plugin.Instance.Safe(delegate
				{
					BindRows();
				});
			});
			RectTransform val8 = Scroll((Transform)(object)val, "EntryDetails", 516f, 218f, 460f, height);
			detailScroll = ((Component)((Transform)val8).parent).GetComponent<ScrollRect>();
			detailIcon = ((Component)Box((Transform)(object)val8, "Icon", Color.white)).GetComponent<Image>();
			Place(((Graphic)detailIcon).rectTransform, 10f, 10f, 64f, 64f);
			detailIcon.preserveAspect = true;
			((Component)detailIcon).gameObject.SetActive(false);
			detailTitle = Label((Transform)(object)val8, "", 25, skin.ItemTitle);
			Place(detailTitle.rectTransform, 86f, 16f, 210f, 68f);
			inspectButton = Button((Transform)(object)val8, "Inspect", new UnityAction(ToggleInspection));
			inspectButtonText = ((Component)inspectButton).GetComponentInChildren<TMP_Text>();
			Place(((Component)inspectButton).GetComponent<RectTransform>(), 306f, 24f, 112f, 38f);
			((Component)inspectButton).gameObject.SetActive(false);
			UITooltip obj6 = ((Component)inspectButton).gameObject.AddComponent<UITooltip>();
			obj6.m_topic = "Inspect Item";
			obj6.m_text = "Show the live quality-one item stats\nused by Valheim's crafting panel.\nNo item is created or added to inventory.";
			obj6.m_tooltipPrefab = ((Component)owner.m_repairButton).GetComponent<UITooltip>()?.m_tooltipPrefab;
			detail = Label((Transform)(object)val8, "Select an entry to read its details.", 18);
			Place(detail.rectTransform, 10f, 90f, 418f, 340f);
			detail.textWrappingMode = (TextWrappingModes)1;
			CreateNearbyResetPanel(val);
			CreateFilterPanel(val);
			panel.SetActive(false);
			UpdateNearbyDiscoveryButton(force: true);
			ready = true;
		}

		private void CreateLauncher(InventoryGui gui)
		{
			//IL_000b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0011: Expected O, but got Unknown
			//IL_0019: Unknown result type (might be due to invalid IL or missing references)
			//IL_001e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0044: Unknown result type (might be due to invalid IL or missing references)
			//IL_0061: Unknown result type (might be due to invalid IL or missing references)
			//IL_0066: Unknown result type (might be due to invalid IL or missing references)
			//IL_006c: Expected O, but got Unknown
			//IL_00a8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b3: Expected O, but got Unknown
			//IL_00f2: Unknown result type (might be due to invalid IL or missing references)
			//IL_0107: Unknown result type (might be due to invalid IL or missing references)
			//IL_010d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0113: Expected O, but got Unknown
			RectTransform val = (RectTransform)((Component)gui.m_repairButton).transform;
			Rect rect = val.rect;
			Vector2 offset = default(Vector2);
			((Vector2)(ref offset))..ctor(0f, 0f - (((Rect)(ref rect)).height + 20f));
			RectTransform val2 = Box(gui.m_repairPanel.parent, "DiscoveryJournalFlap", Color.white, skin.RepairBacking);
			CopyRect(val2, (RectTransform)gui.m_repairPanel, offset);
			((Transform)val2).SetSiblingIndex(gui.m_repairPanel.GetSiblingIndex());
			((Graphic)((Component)val2).GetComponent<Image>()).raycastTarget = false;
			launcherBacking = ((Component)val2).gameObject;
			Button val3 = Button(((Transform)val).parent, "Journal", new UnityAction(Open));
			launcher = ((Component)val3).gameObject;
			((Object)launcher).name = "DiscoveryJournalButton";
			JournalSkin.CopyImage(launcher.GetComponent<Image>(), skin.RepairButton);
			((Selectable)val3).colors = ((Selectable)gui.m_repairButton).colors;
			CopyRect((RectTransform)launcher.transform, val, offset);
			launcherText = launcher.GetComponentInChildren<TMP_Text>();
			launcherText.fontSize = 14f;
			launcherText.textWrappingMode = (TextWrappingModes)0;
			UITooltip obj = launcher.AddComponent<UITooltip>();
			obj.m_topic = "Discovery Journal";
			obj.m_text = "Browse the recipes and build pieces your character knows.";
			obj.m_tooltipPrefab = ((Component)gui.m_repairButton).GetComponent<UITooltip>()?.m_tooltipPrefab;
		}

		private void Open()
		{
			if (!ready)
			{
				return;
			}
			Close();
			Journal.Refresh();
			RectTransform[] array = (RectTransform[])(object)new RectTransform[4] { owner.m_player, owner.m_crafting, owner.m_container, owner.m_info };
			foreach (RectTransform val in array)
			{
				if (Object.op_Implicit((Object)(object)val))
				{
					CanvasGroup val2 = ((Component)val).gameObject.AddComponent<CanvasGroup>();
					val2.interactable = false;
					val2.blocksRaycasts = false;
					val2.alpha = 0.3f;
					inputGuards.Add(val2);
				}
			}
			panel.SetActive(true);
			panel.transform.SetAsLastSibling();
			ClearDetails();
			selection.Leave();
			detailScroll.verticalNormalizedPosition = 1f;
			Render(resetScroll: true);
		}

		internal void Close()
		{
			AcknowledgeSelection();
			if (Object.op_Implicit((Object)(object)searchDelay))
			{
				searchDelay.Cancel();
			}
			if (Object.op_Implicit((Object)(object)filterMaterialDelay))
			{
				filterMaterialDelay.Cancel();
			}
			if (Object.op_Implicit((Object)(object)panel))
			{
				panel.SetActive(false);
			}
			if (Object.op_Implicit((Object)(object)resetPanel))
			{
				resetPanel.SetActive(false);
			}
			if (Object.op_Implicit((Object)(object)filterPanel))
			{
				filterPanel.SetActive(false);
			}
			if (Object.op_Implicit((Object)(object)search))
			{
				search.DeactivateInputField(false);
			}
			foreach (CanvasGroup inputGuard in inputGuards)
			{
				if (Object.op_Implicit((Object)(object)inputGuard))
				{
					inputGuard.alpha = 1f;
					inputGuard.interactable = true;
					inputGuard.blocksRaycasts = true;
					Object.Destroy((Object)(object)inputGuard);
				}
			}
			inputGuards.Clear();
		}

		internal void Tick()
		{
			if (!Object.op_Implicit((Object)(object)launcher))
			{
				return;
			}
			launcher.SetActive(InventoryGui.IsVisible());
			if (Object.op_Implicit((Object)(object)launcherBacking))
			{
				launcherBacking.SetActive(InventoryGui.IsVisible());
			}
			launcherText.text = "Journal\n" + Journal.UnreadCount;
			UpdateNearbyDiscoveryButton();
			if (Journal.State != null && filterCharacterId != Journal.State.CharacterId)
			{
				filterCharacterId = Journal.State.CharacterId;
				selectedBiome = (selectedLocation = (selectedCategory = (materialQuery = "")));
				if (Object.op_Implicit((Object)(object)filterMaterialInput))
				{
					filterMaterialInput.SetTextWithoutNotify("");
				}
				UpdateFilterDisplay();
			}
			if (!InventoryGui.IsVisible())
			{
				Close();
			}
			if (Object.op_Implicit((Object)(object)panel) && panel.activeSelf && revision != Journal.Revision)
			{
				Render();
			}
		}

		private void ToggleNearbyDiscovery()
		{
			Plugin instance = Plugin.Instance;
			if (Object.op_Implicit((Object)(object)instance))
			{
				instance.SetNearbyDiscovery(!instance.LearnStorage.Value);
				UpdateNearbyDiscoveryButton(force: true);
			}
		}

		private void CreateFilterPanel(RectTransform root)
		{
			//IL_0007: Unknown result type (might be due to invalid IL or missing references)
			//IL_0088: Unknown result type (might be due to invalid IL or missing references)
			//IL_0093: Expected O, but got Unknown
			//IL_00fe: Unknown result type (might be due to invalid IL or missing references)
			//IL_010a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0114: Expected O, but got Unknown
			//IL_0114: Expected O, but got Unknown
			//IL_0139: Unknown result type (might be due to invalid IL or missing references)
			//IL_0145: Unknown result type (might be due to invalid IL or missing references)
			//IL_014f: Expected O, but got Unknown
			//IL_014f: Expected O, but got Unknown
			//IL_0168: Unknown result type (might be due to invalid IL or missing references)
			//IL_0174: Unknown result type (might be due to invalid IL or missing references)
			//IL_017e: Expected O, but got Unknown
			//IL_017e: Expected O, but got Unknown
			//IL_018c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0197: Expected O, but got Unknown
			//IL_01c3: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ce: Expected O, but got Unknown
			RectTransform val = Box((Transform)(object)root, "RecipeFilterPanel", Color.white, skin.Panel);
			Place(val, 180f, 108f, 640f, 424f);
			filterPanel = ((Component)val).gameObject;
			Place(Label((Transform)(object)val, "Filter Known Recipes", 27, skin.Title).rectTransform, 24f, 18f, 430f, 42f);
			Place(((Component)Button((Transform)(object)val, "Done", new UnityAction(CloseFilters))).GetComponent<RectTransform>(), 504f, 20f, 112f, 36f);
			TMP_Text obj = Label((Transform)(object)val, "Choices come only from entries this character already knows. Biome is the latest progression tier needed to unlock the recipe.", 16);
			Place(obj.rectTransform, 24f, 66f, 592f, 48f);
			obj.textWrappingMode = (TextWrappingModes)1;
			CreateFilterSelector((Transform)(object)val, "Biome", 122f, out filterBiomeText, (UnityAction)delegate
			{
				CycleFilter(biomeChoices, ref selectedBiome, -1);
			}, (UnityAction)delegate
			{
				CycleFilter(biomeChoices, ref selectedBiome, 1);
			});
			CreateMaterialFilter((Transform)(object)val, 178f);
			CreateFilterSelector((Transform)(object)val, "Craft / Build At", 234f, out filterLocationText, (UnityAction)delegate
			{
				CycleFilter(locationChoices, ref selectedLocation, -1);
			}, (UnityAction)delegate
			{
				CycleFilter(locationChoices, ref selectedLocation, 1);
			});
			CreateFilterSelector((Transform)(object)val, "Category", 290f, out filterCategoryText, (UnityAction)delegate
			{
				CycleFilter(categoryChoices, ref selectedCategory, -1);
			}, (UnityAction)delegate
			{
				CycleFilter(categoryChoices, ref selectedCategory, 1);
			});
			Place(((Component)Button((Transform)(object)val, "Clear Filters", new UnityAction(ClearFilters))).GetComponent<RectTransform>(), 24f, 362f, 190f, 40f);
			Place(((Component)Button((Transform)(object)val, "Apply & Close", new UnityAction(CloseFilters))).GetComponent<RectTransform>(), 426f, 362f, 190f, 40f);
			filterPanel.SetActive(false);
		}

		private void CreateMaterialFilter(Transform parent, float y)
		{
			//IL_0036: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e1: Unknown result type (might be due to invalid IL or missing references)
			Place(Label(parent, "Required Material", 17).rectTransform, 24f, y + 7f, 170f, 32f);
			RectTransform val = Box(parent, "MaterialFilter", Color.white, skin.Inset);
			Place(val, 198f, y, 418f, 38f);
			filterMaterialInput = ((Component)val).gameObject.AddComponent<TMP_InputField>();
			TMP_Text val2 = Label((Transform)(object)val, "", 17);
			Stretch(val2.rectTransform, 10f, 5f, 10f, 5f);
			TMP_Text val3 = Label((Transform)(object)val, "Any known required material", 17);
			Stretch(val3.rectTransform, 10f, 5f, 10f, 5f);
			((Graphic)val3).color = new Color(0.6f, 0.6f, 0.55f);
			filterMaterialInput.textViewport = val;
			filterMaterialInput.textComponent = val2;
			filterMaterialInput.placeholder = (Graphic)(object)val3;
			filterMaterialInput.lineType = (LineType)0;
			filterMaterialDelay = filterPanel.AddComponent<JournalSearchDelay>();
			filterMaterialDelay.Elapsed = delegate
			{
				materialQuery = filterMaterialInput.text.Trim();
				UpdateFilterDisplay();
				Render(resetScroll: true);
			};
			((UnityEvent<string>)(object)filterMaterialInput.onValueChanged).AddListener((UnityAction<string>)delegate
			{
				filterMaterialDelay.Schedule();
			});
		}

		private void CreateFilterSelector(Transform parent, string label, float y, out TMP_Text value, UnityAction previous, UnityAction next)
		{
			//IL_0061: Unknown result type (might be due to invalid IL or missing references)
			Place(Label(parent, label, 17).rectTransform, 24f, y + 7f, 170f, 32f);
			Place(((Component)Button(parent, "<", previous)).GetComponent<RectTransform>(), 198f, y, 46f, 38f);
			RectTransform val = Box(parent, label + "Value", Color.white, skin.Inset);
			Place(val, 252f, y, 310f, 38f);
			value = Label((Transform)(object)val, "Any", 17);
			Stretch(value.rectTransform, 8f, 5f, 8f, 5f);
			value.alignment = (TextAlignmentOptions)514;
			Place(((Component)Button(parent, ">", next)).GetComponent<RectTransform>(), 570f, y, 46f, 38f);
		}

		private void OpenFilters()
		{
			if (Object.op_Implicit((Object)(object)resetPanel))
			{
				resetPanel.SetActive(false);
			}
			BuildFilterChoices();
			filterPanel.SetActive(true);
			filterPanel.transform.SetAsLastSibling();
			filterMaterialInput.SetTextWithoutNotify(materialQuery);
			UpdateFilterDisplay();
		}

		private void CloseFilters()
		{
			if (Object.op_Implicit((Object)(object)filterPanel))
			{
				filterPanel.SetActive(false);
			}
			if (Object.op_Implicit((Object)(object)filterMaterialDelay))
			{
				filterMaterialDelay.Cancel();
			}
		}

		private static List<FilterChoice> WithAny(IEnumerable<FilterChoice> choices)
		{
			return new FilterChoice[1]
			{
				new FilterChoice
				{
					Key = "",
					Label = "Any"
				}
			}.Concat(choices).ToList();
		}

		private void BuildFilterChoices()
		{
			biomeChoices = WithAny((from biome in (from biome in NearbyBiomeResolver.SingleBiomes()
					where Journal.Entries.Any((JournalEntry entry) => (entry.BiomeMask & biome) != 0)
					select biome).OrderByDescending(NearbyBiomeResolver.ProgressionRank)
				select new FilterChoice
				{
					Key = biome.ToString(),
					Label = NearbyBiomeResolver.Name(biome)
				}).Concat((!Journal.Entries.Any((JournalEntry entry) => entry.HasUnresolvedBiome)) ? ((IEnumerable<FilterChoice>)Array.Empty<FilterChoice>()) : ((IEnumerable<FilterChoice>)new FilterChoice[1]
			{
				new FilterChoice
				{
					Key = "-1",
					Label = "Unresolved / Modded"
				}
			})));
			locationChoices = WithAny(from value in (from value in Journal.Entries.SelectMany((JournalEntry entry) => entry.Locations)
					where !string.IsNullOrEmpty(value)
					select value).Distinct<string>(StringComparer.CurrentCultureIgnoreCase).OrderBy<string, string>((string value) => value, StringComparer.CurrentCultureIgnoreCase)
				select new FilterChoice
				{
					Key = value,
					Label = value
				});
			categoryChoices = WithAny(from value in (from entry in Journal.Entries
					select entry.Category into value
					where !string.IsNullOrEmpty(value)
					select value).Distinct<string>(StringComparer.CurrentCultureIgnoreCase).OrderBy<string, string>((string value) => value, StringComparer.CurrentCultureIgnoreCase)
				select new FilterChoice
				{
					Key = value,
					Label = value
				});
			selectedBiome = KeepChoice(biomeChoices, selectedBiome);
			selectedLocation = KeepChoice(locationChoices, selectedLocation);
			selectedCategory = KeepChoice(categoryChoices, selectedCategory);
		}

		private static string KeepChoice(List<FilterChoice> choices, string selected)
		{
			if (!choices.Any((FilterChoice choice) => choice.Key == selected))
			{
				return "";
			}
			return selected;
		}

		private void CycleFilter(List<FilterChoice> choices, ref string selected, int direction)
		{
			if (choices == null || choices.Count == 0)
			{
				return;
			}
			int num = -1;
			for (int i = 0; i < choices.Count; i++)
			{
				if (choices[i].Key == selected)
				{
					num = i;
					break;
				}
			}
			if (num < 0)
			{
				num = 0;
			}
			selected = choices[(num + direction + choices.Count) % choices.Count].Key;
			resetArmed = false;
			UpdateFilterDisplay();
			Render(resetScroll: true);
		}

		internal void OpenFromHotkey()
		{
			if (!IsOpen)
			{
				Open();
			}
		}

		private void ClearFilters()
		{
			selectedBiome = (selectedLocation = (selectedCategory = ""));
			materialQuery = "";
			if (Object.op_Implicit((Object)(object)filterMaterialInput))
			{
				filterMaterialInput.SetTextWithoutNotify("");
			}
			UpdateFilterDisplay();
			Render(resetScroll: true);
		}

		private void UpdateFilterDisplay()
		{
			//IL_00e2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e7: Unknown result type (might be due to invalid IL or missing references)
			//IL_0104: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ee: Unknown result type (might be due to invalid IL or missing references)
			//IL_0114: Unknown result type (might be due to invalid IL or missing references)
			if (Object.op_Implicit((Object)(object)filtersButtonText))
			{
				filterBiomeText.text = ChoiceLabel(biomeChoices, selectedBiome);
				filterLocationText.text = ChoiceLabel(locationChoices, selectedLocation);
				filterCategoryText.text = ChoiceLabel(categoryChoices, selectedCategory);
				int num = new string[4] { selectedBiome, materialQuery, selectedLocation, selectedCategory }.Count((string value) => !string.IsNullOrEmpty(value));
				filtersButtonText.text = ((num == 0) ? "Filters" : ("Filters (" + num + ")"));
				ColorBlock colors = ((Selectable)filtersButton).colors;
				((ColorBlock)(ref colors)).normalColor = (Color)((num > 0) ? new Color(1f, 0.76f, 0.38f) : Color.white);
				((Selectable)filtersButton).colors = colors;
			}
		}

		private static string ChoiceLabel(List<FilterChoice> choices, string selected)
		{
			return choices?.FirstOrDefault((FilterChoice choice) => choice.Key == selected)?.Label ?? "Any";
		}

		private void ValidateActiveFilters()
		{
			bool flag = false;
			if (!string.IsNullOrEmpty(selectedBiome))
			{
				int result;
				int biome = (int.TryParse(selectedBiome, out result) ? result : 0);
				if (!Journal.Entries.Any((JournalEntry entry) => (biome != -1) ? ((entry.BiomeMask & biome) != 0) : entry.HasUnresolvedBiome))
				{
					selectedBiome = "";
					flag = true;
				}
			}
			if (!string.IsNullOrEmpty(selectedLocation) && !Journal.Entries.Any((JournalEntry entry) => entry.Locations.Contains<string>(selectedLocation, StringComparer.CurrentCultureIgnoreCase)))
			{
				selectedLocation = "";
				flag = true;
			}
			if (!string.IsNullOrEmpty(selectedCategory) && !Journal.Entries.Any((JournalEntry entry) => string.Equals(entry.Category, selectedCategory, StringComparison.CurrentCultureIgnoreCase)))
			{
				selectedCategory = "";
				flag = true;
			}
			if (flag)
			{
				UpdateFilterDisplay();
			}
		}

		private void CreateNearbyResetPanel(RectTransform root)
		{
			//IL_0007: Unknown result type (might be due to invalid IL or missing references)
			//IL_0088: Unknown result type (might be due to invalid IL or missing references)
			//IL_0093: Expected O, but got Unknown
			//IL_00c0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00cb: Expected O, but got Unknown
			//IL_0103: Unknown result type (might be due to invalid IL or missing references)
			//IL_010e: Expected O, but got Unknown
			//IL_014d: Unknown result type (might be due to invalid IL or missing references)
			//IL_01da: Unknown result type (might be due to invalid IL or missing references)
			//IL_01e5: Expected O, but got Unknown
			//IL_0264: Unknown result type (might be due to invalid IL or missing references)
			//IL_026f: Expected O, but got Unknown
			//IL_0302: Unknown result type (might be due to invalid IL or missing references)
			//IL_030d: Expected O, but got Unknown
			RectTransform val = Box((Transform)(object)root, "RecipeResetPanel", Color.white, skin.Panel);
			Place(val, 130f, 82f, 740f, 520f);
			resetPanel = ((Component)val).gameObject;
			Place(Label((Transform)(object)val, "Reset Recipes", 27, skin.Title).rectTransform, 24f, 18f, 500f, 42f);
			Place(((Component)Button((Transform)(object)val, "Close", new UnityAction(CloseNearbyReset))).GetComponent<RectTransform>(), 604f, 20f, 112f, 36f);
			resetNearbyTab = Button((Transform)(object)val, "Delete Nearby", (UnityAction)delegate
			{
				SetResetMode(wholeBiome: false);
			});
			Place(((Component)resetNearbyTab).GetComponent<RectTransform>(), 24f, 68f, 210f, 38f);
			resetBiomeTab = Button((Transform)(object)val, "Delete Whole Biome", (UnityAction)delegate
			{
				SetResetMode(wholeBiome: true);
			});
			Place(((Component)resetBiomeTab).GetComponent<RectTransform>(), 244f, 68f, 250f, 38f);
			RectTransform obj = Box((Transform)(object)val, "ResetTabBorder", new Color(0.8f, 0.55f, 0.12f), skin.TabBorder);
			Place(obj, 16f, 105f, 708f, 2f);
			((Graphic)((Component)obj).GetComponent<Image>()).raycastTarget = false;
			resetIntro = Label((Transform)(object)val, "", 17);
			Place(resetIntro.rectTransform, 24f, 120f, 692f, 62f);
			resetIntro.textWrappingMode = (TextWrappingModes)1;
			Place(((Component)Button((Transform)(object)val, "<", (UnityAction)delegate
			{
				CycleNearbyReset(-1);
			})).GetComponent<RectTransform>(), 24f, 188f, 54f, 36f);
			resetBiomeText = Label((Transform)(object)val, "No tracked discoveries", 21, skin.ItemTitle);
			Place(resetBiomeText.rectTransform, 88f, 190f, 564f, 34f);
			resetBiomeText.alignment = (TextAlignmentOptions)514;
			Place(((Component)Button((Transform)(object)val, ">", (UnityAction)delegate
			{
				CycleNearbyReset(1);
			})).GetComponent<RectTransform>(), 662f, 188f, 54f, 36f);
			RectTransform parent = Scroll((Transform)(object)val, "ResetDetails", 24f, 236f, 692f, 204f);
			resetDetails = Label((Transform)(object)parent, "", 17);
			Place(resetDetails.rectTransform, 12f, 10f, 650f, 176f);
			resetDetails.textWrappingMode = (TextWrappingModes)1;
			resetAction = Button((Transform)(object)val, "Reset", new UnityAction(ConfirmNearbyReset));
			Place(((Component)resetAction).GetComponent<RectTransform>(), 378f, 458f, 338f, 42f);
			resetActionText = ((Component)resetAction).GetComponentInChildren<TMP_Text>();
			resetPanel.SetActive(false);
		}

		private void OpenNearbyReset()
		{
			if (Object.op_Implicit((Object)(object)filterPanel))
			{
				filterPanel.SetActive(false);
			}
			resetIndex = 0;
			resetArmed = false;
			resetWholeBiome = false;
			resetPanel.SetActive(true);
			resetPanel.transform.SetAsLastSibling();
			RenderNearbyReset();
		}

		private void SetResetMode(bool wholeBiome)
		{
			resetWholeBiome = wholeBiome;
			resetIndex = 0;
			resetArmed = false;
			RenderNearbyReset();
		}

		private void CloseNearbyReset()
		{
			resetArmed = false;
			if (Object.op_Implicit((Object)(object)resetPanel))
			{
				resetPanel.SetActive(false);
			}
		}

		private void CycleNearbyReset(int direction)
		{
			if (resetOptions.Count != 0)
			{
				resetIndex = (resetIndex + direction + resetOptions.Count) % resetOptions.Count;
				resetArmed = false;
				RenderNearbyReset(reload: false);
			}
		}

		private void RenderNearbyReset(bool reload = true)
		{
			//IL_006d: 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_0095: Unknown result type (might be due to invalid IL or missing references)
			//IL_007f: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a6: Unknown result type (might be due to invalid IL or missing references)
			//IL_03d9: Unknown result type (might be due to invalid IL or missing references)
			//IL_03ee: Unknown result type (might be due to invalid IL or missing references)
			//IL_0408: Unknown result type (might be due to invalid IL or missing references)
			//IL_041d: Unknown result type (might be due to invalid IL or missing references)
			//IL_042d: Unknown result type (might be due to invalid IL or missing references)
			if (reload)
			{
				resetOptions = (resetWholeBiome ? Journal.LegacyResetOptions() : Journal.NearbyResetOptions());
			}
			var array = new[]
			{
				new
				{
					Button = resetNearbyTab,
					Selected = !resetWholeBiome
				},
				new
				{
					Button = resetBiomeTab,
					Selected = resetWholeBiome
				}
			};
			foreach (var anon in array)
			{
				ColorBlock colors = ((Selectable)anon.Button).colors;
				((ColorBlock)(ref colors)).normalColor = (Color)(anon.Selected ? new Color(1f, 0.76f, 0.38f) : Color.white);
				((Selectable)anon.Button).colors = colors;
			}
			if (Object.op_Implicit((Object)(object)resetIntro))
			{
				resetIntro.text = (resetWholeBiome ? "Deletes all confidently mapped material knowledge for one biome, including discoveries earned normally. Use this legacy recovery option only when you accept that broader reset." : "Deletes only tracked nearby-storage discoveries for one biome. Reset turns nearby discovery off, and materials later acquired in personal inventory are protected.");
			}
			if (resetIndex >= resetOptions.Count)
			{
				resetIndex = 0;
			}
			if (resetOptions.Count == 0)
			{
				resetBiomeText.text = (resetWholeBiome ? "No mapped biome knowledge" : "No resettable nearby discoveries");
				resetDetails.text = (resetWholeBiome ? "No currently known materials could be mapped confidently to one biome. Cross-biome and unresolved materials are never guessed." : ((Journal.UnresolvedNearbyCount > 0) ? (Journal.UnresolvedNearbyCount + " nearby discoveries have cross-biome or unresolved sources and are being left untouched.") : "No active nearby discoveries have been recorded for this character yet."));
				((Selectable)resetAction).interactable = false;
				resetActionText.text = "Nothing to Reset";
				return;
			}
			NearbyResetSummary nearbyResetSummary = resetOptions[resetIndex];
			resetBiomeText.text = nearbyResetSummary.Biome + "  (" + (resetIndex + 1) + "/" + resetOptions.Count + ")";
			string text = string.Join("\n", nearbyResetSummary.Materials.Select((string key) => "• " + JournalCatalog.Localize(key)));
			string text2 = string.Join("\n", nearbyResetSummary.KnowledgeNames.Select((string name) => "• " + name));
			resetDetails.text = nearbyResetSummary.Materials.Count + (resetWholeBiome ? " known biome material" : " nearby material") + ((nearbyResetSummary.Materials.Count == 1) ? "" : "s") + " and up to " + nearbyResetSummary.KnowledgeKeys.Count + " recipe/build entr" + ((nearbyResetSummary.KnowledgeKeys.Count == 1) ? "y" : "ies") + " are in this review.\n\nMaterials:\n" + text + ((nearbyResetSummary.KnowledgeNames.Count > 0) ? ("\n\nAffected entries:\n" + text2) : "") + ((!resetWholeBiome && Journal.UnresolvedNearbyCount > 0) ? ("\n\n" + Journal.UnresolvedNearbyCount + " unresolved or cross-biome discoveries will not be changed.") : "") + (resetWholeBiome ? "\n\nWARNING: This mode can remove recipes learned without nearby discovery." : "");
			resetDetails.rectTransform.sizeDelta = new Vector2(650f, Math.Max(176f, resetDetails.GetPreferredValues(resetDetails.text, 650f, 10000f).y + 12f));
			((RectTransform)resetDetails.transform.parent).sizeDelta = new Vector2(0f, resetDetails.rectTransform.sizeDelta.y + 20f);
			((Selectable)resetAction).interactable = true;
			resetActionText.text = (resetArmed ? ("Confirm Delete " + nearbyResetSummary.Biome) : ("Review & Delete " + nearbyResetSummary.Biome));
		}

		private void ConfirmNearbyReset()
		{
			if (resetOptions.Count == 0)
			{
				return;
			}
			if (!resetArmed)
			{
				resetArmed = true;
				RenderNearbyReset(reload: false);
				return;
			}
			NearbyResetSummary nearbyResetSummary = resetOptions[resetIndex];
			int num = (resetWholeBiome ? Journal.ResetLegacyBiome(nearbyResetSummary.BiomeMask) : Journal.ResetNearbyBiome(nearbyResetSummary.BiomeMask));
			resetArmed = false;
			if (Object.op_Implicit((Object)(object)Player.m_localPlayer))
			{
				((Character)Player.m_localPlayer).Message((MessageType)1, resetWholeBiome ? ("Reset " + num + " known " + nearbyResetSummary.Biome + " materials") : ("Reset " + num + " nearby " + nearbyResetSummary.Biome + " discoveries"), 0, (Sprite)null, false);
			}
			RenderNearbyReset();
			Render(resetScroll: true);
		}

		private void UpdateNearbyDiscoveryButton(bool force = false)
		{
			//IL_0094: 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_00b5: Unknown result type (might be due to invalid IL or missing references)
			//IL_009f: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c5: Unknown result type (might be due to invalid IL or missing references)
			Plugin instance = Plugin.Instance;
			if (!Object.op_Implicit((Object)(object)nearbyDiscoveryButton) || !Object.op_Implicit((Object)(object)nearbyDiscoveryText) || !Object.op_Implicit((Object)(object)instance))
			{
				return;
			}
			bool value = instance.LearnStorage.Value;
			float value2 = instance.Radius.Value;
			if (force || value != displayedNearbyDiscovery || !Mathf.Approximately(value2, displayedRadius))
			{
				displayedNearbyDiscovery = value;
				displayedRadius = value2;
				nearbyDiscoveryText.text = "Nearby Discovery: " + (value ? "ON" : "OFF");
				ColorBlock colors = ((Selectable)nearbyDiscoveryButton).colors;
				((ColorBlock)(ref colors)).normalColor = (Color)(value ? new Color(1f, 0.76f, 0.38f) : Color.white);
				((Selectable)nearbyDiscoveryButton).colors = colors;
				UITooltip component = ((Component)nearbyDiscoveryButton).GetComponent<UITooltip>();
				if (Object.op_Implicit((Object)(object)component))
				{
					component.m_text = (value ? "Enabled" : "Disabled") + ". Accessible player-built storage within " + value2.ToString("0.#") + " m can teach this character about items. Click to " + (value ? "disable." : "enable. A seven-second grace period applies.");
				}
			}
		}

		private void Select(JournalEntry entry)
		{
			Plugin.Instance.Safe(delegate
			{
				string text = selection.Select(entry.Id);
				if (text != null)
				{
					Journal.Read(new string[1] { text });
				}
				inspecting = false;
				ShowDetails(entry);
				Render();
			});
		}

		private void ToggleInspection()
		{
			JournalEntry journalEntry = Journal.Entries.FirstOrDefault((JournalEntry candidate) => candidate.Id == selectedId);
			if (journalEntry?.InspectItem?.m_shared != null)
			{
				inspecting = !inspecting;
				ShowDetails(journalEntry);
			}
		}

		private void ShowDetails(JournalEntry entry)
		{
			//IL_0142: Unknown result type (might be due to invalid IL or missing references)
			//IL_0157: Unknown result type (might be due to invalid IL or missing references)
			//IL_0171: Unknown result type (might be due to invalid IL or missing references)
			//IL_0186: Unknown result type (might be due to invalid IL or missing references)
			//IL_018b: 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)
			//IL_01b4: Unknown result type (might be due to invalid IL or missing references)
			EntryRecord entryRecord = Journal.State.Find(entry.Id);
			bool flag = entry.InspectItem?.m_shared != null;
			if (!flag)
			{
				inspecting = false;
			}
			detailTitle.text = entry.Name;
			((Component)inspectButton).gameObject.SetActive(flag);
			inspectButtonText.text = (inspecting ? "Recipe" : "Inspect");
			detail.richText = inspecting;
			if (inspecting)
			{
				int num = 1;
				string tooltip = ItemData.GetTooltip(entry.InspectItem, num, true, (float)Game.m_worldLevel, -1, false);
				detail.text = "<color=#FFC15A>" + JournalRules.InspectionHeading(num, entry.InspectItem.m_shared.m_maxQuality > 1) + "</color>\n\n" + JournalCatalog.Localize(tooltip);
			}
			else
			{
				detail.text = entry.Detail + ((entryRecord != null && entryRecord.Source == DiscoverySource.NearbyStorage) ? "\n\nDiscovered from nearby storage" : "");
			}
			detail.rectTransform.sizeDelta = new Vector2(418f, Math.Max(340f, detail.GetPreferredValues(detail.text, 418f, 10000f).y + 15f));
			RectTransform val = (RectTransform)detail.transform.parent;
			Rect rect = detailScroll.viewport.rect;
			val.sizeDelta = new Vector2(0f, Math.Max(((Rect)(ref rect)).height, detail.rectTransform.sizeDelta.y + 100f));
			detailIcon.sprite = entry.Icon;
			((Component)detailIcon).gameObject.SetActive(Object.op_Implicit((Object)(object)entry.Icon));
			detailScroll.verticalNormalizedPosition = 1f;
		}

		private void ClearDetails()
		{
			//IL_007f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0094: 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_00a1: Unknown result type (might be due to invalid IL or missing references)
			inspecting = false;
			detail.richText = false;
			detail.text = "Select an entry to read its details.";
			detailTitle.text = "";
			((Component)detailIcon).gameObject.SetActive(false);
			if (Object.op_Implicit((Object)(object)inspectButton))
			{
				((Component)inspectButton).gameObject.SetActive(false);
			}
			if (Object.op_Implicit((Object)(object)detailScroll))
			{
				RectTransform val = (RectTransform)detail.transform.parent;
				Rect rect = detailScroll.viewport.rect;
				val.sizeDelta = new Vector2(0f, ((Rect)(ref rect)).height);
				detailScroll.verticalNormalizedPosition = 1f;
			}
		}

		private void AcknowledgeSelection()
		{
			string text = selection.Leave();
			if (text != null)
			{
				Journal.Read(new string[1] { text });
			}
		}

		private void Render(bool resetScroll = false)
		{
			//IL_0100: Unknown result type (might be due to invalid IL or missing references)
			//IL_0105: Unknown result type (might be due to invalid IL or missing references)
			//IL_012e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0118: Unknown result type (might be due to invalid IL or missing references)
			//IL_013f: Unknown result type (might be due to invalid IL or missing references)
			//IL_01b1: Unknown result type (might be due to invalid IL or missing references)
			//IL_01b6: Unknown result type (might be due to invalid IL or missing references)
			//IL_01e2: Unknown result type (might be due to invalid IL or missing references)
			//IL_01fd: Unknown result type (might be due to invalid IL or missing references)
			//IL_0202: Unknown result type (might be due to invalid IL or missing references)
			//IL_0209: Unknown result type (might be due to invalid IL or missing references)
			//IL_0220: Unknown result type (might be due to invalid IL or missing references)
			//IL_0249: Unknown result type (might be due to invalid IL or missing references)
			if (!ready || !Object.op_Implicit((Object)(object)panel) || Journal.State == null)
			{
				return;
			}
			ValidateActiveFilters();
			if (resetScroll)
			{
				AcknowledgeSelection();
				ClearDetails();
				searchDelay.Cancel();
				appliedSearch = search.text;
			}
			revision = Journal.Revision;
			if (selectedId != null && !Journal.Entries.Any((JournalEntry e) => e.Id == selectedId))
			{
				selection.Leave();
				ClearDetails();
			}
			((Component)filters[JournalFilter.New]).GetComponentInChildren<TMP_Text>().text = "New (" + Journal.UnreadCount + ")";
			foreach (KeyValuePair<JournalFilter, Button> filter in filters)
			{
				ColorBlock colors = ((Selectable)filter.Value).colors;
				((ColorBlock)(ref colors)).normalColor = (Color)((filter.Key == this.filter) ? new Color(1f, 0.76f, 0.38f) : Color.white);
				((Selectable)filter.Value).colors = colors;
			}
			int result;
			int biome = (int.TryParse(selectedBiome, out result) ? result : 0);
			matches = Journal.Entries.Where((JournalEntry e) => JournalRules.Matches(e.Kind, Journal.State.IsUnread(e.Id), this.filter, appliedSearch, e.Name) && JournalRules.MatchesDetails(e.BiomeMask, e.HasUnresolvedBiome, e.MaterialNames, e.Locations, e.Category, biome, materialQuery, selectedLocation, selectedCategory)).ToList();
			updatingList = true;
			try
			{
				Rect rect = listScroll.viewport.rect;
				float height = ((Rect)(ref rect)).height;
				rows.sizeDelta = new Vector2(0f, Math.Max(height, matches.Count * 64));
				listScroll.StopMovement();
				Vector2 anchoredPosition = rows.anchoredPosition;
				anchoredPosition.y = (resetScroll ? 0f : Mathf.Clamp(anchoredPosition.y, 0f, Math.Max(0f, rows.sizeDelta.y - height)));
				rows.anchoredPosition = anchoredPosition;
				((Component)emptyMessage).gameObject.SetActive(matches.Count == 0);
			}
			finally
			{
				updatingList = false;
			}
			BindRows(force: true);
		}

		private RowView CreateRow()
		{
			//IL_0031: Unknown result type (might be due to invalid IL or missing references)
			//IL_003c: Expected O, but got Unknown
			//IL_012c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0171: Unknown result type (might be due to invalid IL or missing references)
			//IL_01fc: Unknown result type (might be due to invalid IL or missing references)
			//IL_0222: Unknown result type (might be due to invalid IL or missing references)
			RowView view = new RowView();
			view.Button = Button((Transform)(object)rows, "", (UnityAction)delegate
			{
				if (view.Entry != null)
				{
					Select(view.Entry);
				}
			}, row: true);
			view.Title = ((Component)view.Button).GetComponentInChildren<TMP_Text>();
			Place(view.Title.rectTransform, 60f, 4f, 310f, 27f);
			view.Title.alignment = (TextAlignmentOptions)4097;
			view.Title.fontSize = 19f;
			view.Title.textWrappingMode = (TextWrappingModes)0;
			view.Summary = Label(((Component)view.Button).transform, "", 16);
			Place(view.Summary.rectTransform, 60f, 32f, 378f, 24f);
			((Graphic)view.Summary).color = new Color(0.85f, 0.79f, 0.65f);
			view.Summary.textWrappingMode = (TextWrappingModes)0;
			RectTransform val = Box(((Component)view.Button).transform, "UnreadBadge", new Color(0.3f, 0.19f, 0.02f, 0.85f));
			Place(val, 387f, 7f, 54f, 22f);
			((Graphic)((Component)val).GetComponent<Image>()).raycastTarget = false;
			view.Badge = ((Component)val).gameObject;
			TMP_Text obj = Label((Transform)(object)val, "NEW", 15);
			Stretch(obj.rectTransform, 2f, 0f, 2f, 0f);
			obj.alignment = (TextAlignmentOptions)514;
			((Graphic)obj).color = new Color(1f, 0.85f, 0.1f);
			view.Icon = ((Component)Box(((Component)view.Button).transform, "Icon", Color.white)).GetComponent<Image>();
			Place(((Graphic)view.Icon).rectTransform, 5f, 5f, 48f, 48f);
			view.Icon.preserveAspect = true;
			return view;
		}

		private void BindRows(bool force = false)
		{
			//IL_0039: Unknown result type (might be due to invalid IL or missing references)
			//IL_004e: 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_0173: Unknown result type (might be due to invalid IL or missing references)
			//IL_0198: Expected O, but got Unknown
			//IL_01cb: Unknown result type (might be due to invalid IL or missing references)
			//IL_01d0: 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_01e8: Unknown result type (might be due to invalid IL or missing references)
			//IL_0205: Unknown result type (might be due to invalid IL or missing references)
			if (!ready || updatingList || !IsOpen || !Object.op_Implicit((Object)(object)listScroll))
			{
				return;
			}
			int count = matches.Count;
			float y = rows.anchoredPosition.y;
			Rect rect = listScroll.viewport.rect;
			JournalWindow journalWindow = new JournalWindow(count, y, ((Rect)(ref rect)).height);
			if (!force && firstBound == journalWindow.First && rowPool.Count >= journalWindow.Count)
			{
				return;
			}
			firstBound = journalWindow.First;
			while (rowPool.Count < journalWindow.Count)
			{
				rowPool.Add(CreateRow());
			}
			for (int i = 0; i < rowPool.Count; i++)
			{
				RowView rowView = rowPool[i];
				bool flag = i < journalWindow.Count;
				((Component)rowView.Button).gameObject.SetActive(flag);
				if (!flag)
				{
					rowView.Entry = null;
					continue;
				}
				JournalEntry journalEntry = matches[journalWindow.First + i];
				if (rowView.Entry?.Id != journalEntry.Id && Object.op_Implicit((Object)(object)EventSystem.current) && (Object)(object)EventSystem.current.currentSelectedGameObject == (Object)(object)((Component)rowView.Button).gameObject)
				{
					EventSystem.current.SetSelectedGameObject((GameObject)null);
				}
				rowView.Entry = journalEntry;
				Place((RectTransform)((Component)rowView.Button).transform, 0f, (journalWindow.First + i) * 64, 450f, 60f);
				rowView.Title.text = journalEntry.Name;
				rowView.Summary.text = journalEntry.Summary.Replace('\n', ' ');
				ColorBlock colors = ((Selectable)rowView.Button).colors;
				((ColorBlock)(ref colors)).normalColor = ((selectedId == journalEntry.Id) ? skin.Selected : Color.clear);
				((Selectable)rowView.Button).colors = colors;
				rowView.Badge.SetActive(Journal.State.IsUnread(journalEntry.Id));
				rowView.Icon.sprite = journalEntry.Icon;
				((Component)rowView.Icon).gameObject.SetActive(Object.op_Implicit((Object)(object)journalEntry.Icon));
			}
		}

		internal void Dispose()
		{
			ready = false;
			Close();
			if (Object.op_Implicit((Object)(object)panel))
			{
				Object.Destroy((Object)(object)panel);
			}
			if (Object.op_Implicit((Object)(object)launcher))
			{
				Object.Destroy((Object)(object)launcher);
			}
			if (Object.op_Implicit((Object)(object)launcherBacking))
			{
				Object.Destroy((Object)(object)launcherBacking);
			}
			filters.Clear();
			rowPool.Clear();
			matches.Clear();
			firstBound = -1;
			panel = (launcher = (launcherBacking = null));
			owner = null;
			revision = -1;
		}

		private RectTransform Box(Transform parent, string name, Color color, Image style = null)
		{
			//IL_002e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0033: Unknown result type (might be due to invalid IL or missing references)
			//IL_0040: Unknown result type (might be due to invalid IL or missing references)
			//IL_0047: Unknown result type (might be due to invalid IL or missing references)
			GameObject val = new GameObject(name, new Type[3]
			{
				typeof(RectTransform),
				typeof(CanvasRenderer),
				typeof(Image)
			});
			val.transform.SetParent(parent, false);
			Image component = val.GetComponent<Image>();
			((Graphic)component).color = color;
			JournalSkin.CopyImage(component, style);
			return val.GetComponent<RectTransform>();
		}

		private TMP_Text Label(Transform parent, string value, int size, TMP_Text style = null)
		{
			//IL_0025: Unknown result type (might be due to invalid IL or missing references)
			//IL_002a: Unknown result type (might be due to invalid IL or missing references)
			GameObject val = new GameObject("Text", new Type[2]
			{
				typeof(RectTransform),
				typeof(CanvasRenderer)
			});
			val.transform.SetParent(parent, false);
			TextMeshProUGUI obj = val.AddComponent<TextMeshProUGUI>();
			JournalSkin.CopyText((TMP_Text)(object)obj, Object.op_Implicit((Object)(object)style) ? style : skin.Body);
			((TMP_Text)obj).fontSize = size;
			((TMP_Text)obj).text = value;
			((Graphic)obj).raycastTarget = false;
			((TMP_Text)obj).richText = false;
			((TMP_Text)obj).alignment = (TextAlignmentOptions)257;
			((TMP_Text)obj).overflowMode = (TextOverflowModes)1;
			return (TMP_Text)(object)obj;
		}

		private Button Button(Transform parent, string title, UnityAction action, bool row = false)
		{
			//IL_0010: Unknown result type (might be due to invalid IL or missing references)
			//IL_0051: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d0: Expected O, but got Unknown
			//IL_0060: Unknown result type (might be due to invalid IL or missing references)
			//IL_0065: Unknown result type