Decompiled source of PlayerHome v1.0.0

PlayerHome.dll

Decompiled 20 hours ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Serialization.Formatters.Binary;
using System.Runtime.Versioning;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using SailwindModdingHelper;
using UnityEngine;
using UnityEngine.Rendering;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETFramework,Version=v4.7.2", FrameworkDisplayName = ".NET Framework 4.7.2")]
[assembly: AssemblyCompany("PlayerHome")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0")]
[assembly: AssemblyProduct("PlayerHome")]
[assembly: AssemblyTitle("PlayerHome")]
[assembly: AssemblyVersion("1.0.0.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 PlayerHome
{
	[BepInPlugin("com.yourname.playerhome", "Player Home", "1.0.0")]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	public class PlayerHomePlugin : BaseUnityPlugin
	{
		public const string GUID = "com.yourname.playerhome";

		public const string NAME = "Player Home";

		public const string VERSION = "1.0.0";

		internal static PlayerHomePlugin I;

		internal static ManualLogSource Log;

		private ConfigEntry<bool> _dumpPrefabs;

		private ConfigEntry<bool> _enableTavernClaim;

		private ConfigEntry<bool> _traceSleep;

		private ConfigEntry<KeyCode> _captureKey;

		private ConfigEntry<KeyCode> _probeKey;

		private ConfigEntry<bool> _persistItems;

		private ConfigEntry<KeyCode> _recordKey;

		private ConfigEntry<KeyCode> _restoreKey;

		private ConfigEntry<float> _clearRadius;

		private ConfigEntry<float> _furnishRadius;

		private ConfigEntry<string> _clearOverrides;

		private readonly Dictionary<string, float> _clearRadiusOverrides = new Dictionary<string, float>();

		private ConfigEntry<string> _excludedTaverns;

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

		private ConfigEntry<string> _priceOverrides;

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

		private readonly List<Home> _homes = new List<Home>();

		private SaveData _save = new SaveData();

		private bool _worldReady;

		private float _claimScanTimer;

		private const float RESCAN_INTERVAL = 2f;

		private float _watchdogTimer;

		private static float CLEAR_RADIUS = 15f;

		private int _lastSeenTavernCount = -1;

		private int _lastTrackedCount = -1;

		private int _btnLayer = -1;

		private static readonly Dictionary<string, int> PriceById = new Dictionary<string, int>
		{
			{ "tavern@32220_-5702", 150 },
			{ "tavern@1990_44155", 40 },
			{ "tavern@-45174_-47290", 120 },
			{ "tavern@10369_35314", 150 },
			{ "tavern@7210_40799", 150 },
			{ "tavern@6675_42432", 75 }
		};

		private static readonly List<KeyValuePair<string, int>> PriceTable = new List<KeyValuePair<string, int>>
		{
			new KeyValuePair<string, int>("nilem", 30),
			new KeyValuePair<string, int>("neverdin", 30),
			new KeyValuePair<string, int>("oasis", 30),
			new KeyValuePair<string, int>("fort aestrin", 100),
			new KeyValuePair<string, int>("malific", 150),
			new KeyValuePair<string, int>("eastwind", 40),
			new KeyValuePair<string, int>("siren", 75),
			new KeyValuePair<string, int>("happy", 150),
			new KeyValuePair<string, int>("dragon", 50),
			new KeyValuePair<string, int>("crab", 80),
			new KeyValuePair<string, int>("sage", 80),
			new KeyValuePair<string, int>("firefly", 150),
			new KeyValuePair<string, int>("dead cove", 75),
			new KeyValuePair<string, int>("chronos", 75),
			new KeyValuePair<string, int>("aestrin", 100)
		};

		private const int DEFAULT_PRICE = 75;

		private static readonly List<KeyValuePair<string, int>> PriceByTown = new List<KeyValuePair<string, int>>
		{
			new KeyValuePair<string, int>("nilem", 30),
			new KeyValuePair<string, int>("neverdin", 30),
			new KeyValuePair<string, int>("oasis", 30),
			new KeyValuePair<string, int>("old ankh", 120),
			new KeyValuePair<string, int>("ankh", 120),
			new KeyValuePair<string, int>("fort aestrin", 100),
			new KeyValuePair<string, int>("malefic", 150),
			new KeyValuePair<string, int>("eastwind", 40),
			new KeyValuePair<string, int>("siren", 75),
			new KeyValuePair<string, int>("happy", 150),
			new KeyValuePair<string, int>("dragon", 50),
			new KeyValuePair<string, int>("crab", 80),
			new KeyValuePair<string, int>("sage", 80),
			new KeyValuePair<string, int>("firefly", 150),
			new KeyValuePair<string, int>("turtle", 75),
			new KeyValuePair<string, int>("dead cove", 75),
			new KeyValuePair<string, int>("chronos", 75)
		};

		private static readonly string[] FurnitureKeywords = new string[24]
		{
			"furniture_table", "furniture chair", "furniture bench", "furniture stool", "bookshelf", "bookcase", "Prefab_BigBarrel", "open barrel", "small keg", "shop stove",
			"stove", "carpet", "painting", "wine", "pickaxe", "table", "bench", "chair", "stool", "keg",
			"barrel", "shelf", "cabinet", "crate"
		};

		private static readonly string[] FurnitureExclude = new string[32]
		{
			"building", "polySurface", "palm", "fence", "dock", "window", "door", "roof", "awning", "wall",
			"stair", "floor", "pillar", "beam", "terrain", "rock", "lamppost", "light post", "lantern", "candle",
			"torch", "streetlight", "shop item", "shop area", "static shop", "(Clone)", "market_stall", "market stall", "trigger", "mooring",
			"crane", "well"
		};

		private static readonly string[] FurnitureHardExclude = new string[1] { "light post a big" };

		private static readonly string[] FurnitureForceInclude = new string[8] { "static shop item", "staticitem", "candle A", "light post a", "building_arab_addon_shelf", "bush_05", "east_market_stall", "ita street banners" };

		private static readonly Dictionary<string, float> _clearRadiusDefaults = new Dictionary<string, float> { { "tavern@-48907_-46062", 10f } };

		private readonly HashSet<int> _killedFurniture = new HashSet<int>();

		private static readonly HashSet<string> _wideBushTaverns = new HashSet<string> { "tavern@43966_-38811" };

		private Transform _graveyard;

		internal static bool TraceSleep
		{
			get
			{
				if ((Object)(object)I != (Object)null && I._traceSleep != null)
				{
					return I._traceSleep.Value;
				}
				return false;
			}
		}

		private string PluginDir => Path.GetDirectoryName(((BaseUnityPlugin)this).Info.Location);

		private string HomesTxtPath => Path.Combine(PluginDir, "homes.txt");

		private static bool OriginReady => (Object)(object)FloatingOriginManager.instance != (Object)null;

		private void Awake()
		{
			//IL_02fd: Unknown result type (might be due to invalid IL or missing references)
			//IL_0307: Expected O, but got Unknown
			//IL_030e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0318: Expected O, but got Unknown
			I = this;
			Log = ((BaseUnityPlugin)this).Logger;
			_dumpPrefabs = ((BaseUnityPlugin)this).Config.Bind<bool>("Setup", "DumpPrefabs", false, "Log candidate building/bed prefab names after a save loads. Turn off when done.");
			_enableTavernClaim = ((BaseUnityPlugin)this).Config.Bind<bool>("Setup", "EnableTavernClaim", true, "Let you claim existing in-world taverns as homes (people + shop cleared, free inn rest kept). This is the main, dependable mode.");
			_traceSleep = ((BaseUnityPlugin)this).Config.Bind<bool>("Rest", "TraceSleep", false, "DIAGNOSTIC: log a detailed frame-by-frame trace of needs + sleep state during ANY tavern rest (owned or not). Use it once on a tavern you DON'T own to capture how the game's paid rest restores you, then send the log. Turn off when done.");
			_captureKey = ((BaseUnityPlugin)this).Config.Bind<KeyCode>("Setup", "CaptureKey", (KeyCode)289, "Press this in-game to log your current spot as a ready-to-paste homes.txt line.");
			_probeKey = ((BaseUnityPlugin)this).Config.Bind<KeyCode>("Setup", "ProbeKey", (KeyCode)290, "DIAGNOSTIC: stand next to a leftover object (e.g. bottles/crate a claimed tavern didn't clear) and press this to log the nearest meshes' exact names, parent chains, positions, and whether each is a live renderer or a baked combine. Use the logged name to extend the furniture keyword list.");
			_persistItems = ((BaseUnityPlugin)this).Config.Bind<bool>("Persistence", "PersistTavernItems", true, "Items you leave inside a tavern you own are remembered and put back when you return (like cargo on a ship). Uses the game's own item serialization; never writes the game's save file. Turn off to disable the feature entirely.");
			_recordKey = ((BaseUnityPlugin)this).Config.Bind<KeyCode>("Persistence", "RecordKey", (KeyCode)288, "TEST: capture the items you've placed in the owned tavern you're standing in, into the mod save. (Auto-capture also runs on game save.)");
			_restoreKey = ((BaseUnityPlugin)this).Config.Bind<KeyCode>("Persistence", "RestoreKey", (KeyCode)287, "TEST: re-spawn the recorded items for the owned tavern you're standing in. (Auto-restore also runs on load.)");
			_clearRadius = ((BaseUnityPlugin)this).Config.Bind<float>("Radii", "ClearRadius", 15f, "How far (metres) from a tavern's anchor a CLAIM clears furniture, shop goods and NPCs. Keep this tight — just the room — so claiming doesn't delete neighbouring market stalls or the merchant/passenger-dude tables nearby.");
			_furnishRadius = ((BaseUnityPlugin)this).Config.Bind<float>("Radii", "FurnishRadius", 14f, "How far (metres) from a tavern's anchor counts as 'your home' for item persistence: items you leave inside this zone are remembered and restored. Made a bit wider than the room so you can furnish an adjacent porch/deck too.");
			_clearOverrides = ((BaseUnityPlugin)this).Config.Bind<string>("Radii", "ClearRadiusOverrides", "", "Per-tavern clear-radius overrides for taverns whose claim reaches something it shouldn't (e.g. a vendor just outside). Format: 'id:metres' pairs separated by ';', where the id is the one shown in the [claim] log line for that tavern. Example: 'tavern@-267_-494:8;tavern@100_200:10'. (As of 0.8.4 these ids are stable across sessions, so an override you set here will keep matching.)");
			_excludedTaverns = ((BaseUnityPlugin)this).Config.Bind<string>("Claiming", "ExcludedTaverns", "tavern@-43697_-44065;tavern@42687_-35046", "Taverns that CANNOT be claimed (no buy option appears). Semicolon-separated list of ids as shown in the [claim] log line. Defaults exclude Gold Rock City and New Port, whose decor is baked into a shared city mesh that can't be cleanly removed. Clear this string to make every tavern claimable again.");
			_priceOverrides = ((BaseUnityPlugin)this).Config.Bind<string>("Claiming", "PriceOverrides", "", "Override the claim price of specific taverns. Semicolon-separated 'key:price' pairs, where key is either the tavern id (tavern@X_Z from the [claim] log) or a word from its port name (e.g. 'neverdin:25'). Use this to fix any tavern that the built-in price table didn't recognise — the assigned price and detected port name are written to the log on load.");
			CLEAR_RADIUS = _clearRadius.Value;
			ParseClearOverrides(_clearOverrides.Value);
			_excludedIds.Clear();
			string[] array = _excludedTaverns.Value.Split(new char[1] { ';' });
			for (int i = 0; i < array.Length; i++)
			{
				string text = array[i].Trim();
				if (text.Length > 0)
				{
					_excludedIds.Add(text);
				}
			}
			_priceOverrideMap.Clear();
			array = _priceOverrides.Value.Split(new char[1] { ';' });
			for (int i = 0; i < array.Length; i++)
			{
				string text2 = array[i].Trim();
				int num = text2.LastIndexOf(':');
				if (num > 0 && int.TryParse(text2.Substring(num + 1).Trim(), out var result))
				{
					_priceOverrideMap[text2.Substring(0, num).Trim().ToLowerInvariant()] = result;
				}
			}
			Harmony.CreateAndPatchAll(typeof(PlayerHomePlugin).Assembly, "com.yourname.playerhome");
			GameEvents.OnSaveLoadPost += (SaveSlotEventHandler)delegate
			{
				OnWorldReady();
			};
			GameEvents.OnGameSave += (GameSaveEventHandler)delegate
			{
				OnGameSave();
			};
			Log.LogInfo((object)"Player Home 1.0.0 loaded.");
		}

		private static Vector3 RealToWorld(Vector3 real)
		{
			//IL_000e: Unknown result type (might be due to invalid IL or missing references)
			//IL_000f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0007: Unknown result type (might be due to invalid IL or missing references)
			if (!OriginReady)
			{
				return real;
			}
			return FloatingOriginManager.instance.RealPosToShiftingPos(real);
		}

		private static Vector3 WorldToReal(Vector3 world)
		{
			//IL_000e: Unknown result type (might be due to invalid IL or missing references)
			//IL_000f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0007: Unknown result type (might be due to invalid IL or missing references)
			if (!OriginReady)
			{
				return world;
			}
			return FloatingOriginManager.instance.ShiftingPosToRealPos(world);
		}

		private void Update()
		{
			//IL_006d: Unknown result type (might be due to invalid IL or missing references)
			//IL_011d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0135: Unknown result type (might be due to invalid IL or missing references)
			//IL_0091: Unknown result type (might be due to invalid IL or missing references)
			//IL_0096: Unknown result type (might be due to invalid IL or missing references)
			//IL_009b: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00cd: Unknown result type (might be due to invalid IL or missing references)
			//IL_00db: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e9: Unknown result type (might be due to invalid IL or missing references)
			//IL_01a6: Unknown result type (might be due to invalid IL or missing references)
			//IL_003f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0044: Unknown result type (might be due to invalid IL or missing references)
			if (OriginReady)
			{
				foreach (Home home3 in _homes)
				{
					if (!((Object)(object)home3.root == (Object)null) && !home3.isClaimedTavern)
					{
						home3.root.transform.position = RealToWorld(home3.realPos);
					}
				}
			}
			if (Input.GetKeyDown(_captureKey.Value) && (Object)(object)Utilities.PlayerTransform != (Object)null)
			{
				Vector3 val = WorldToReal(Utilities.PlayerTransform.position);
				float y = Utilities.PlayerTransform.eulerAngles.y;
				Log.LogInfo((object)"==== PLAYER HOME: spot captured ====");
				Log.LogInfo((object)$"  myhome | TAVERN |  | {val.x:F2} | {val.y:F2} | {val.z:F2} | {y:F0} | 25000 | -1 | 12");
				Log.LogInfo((object)"  (paste that line into homes.txt; change id/prefab/price to taste)");
			}
			if (Input.GetKeyDown(_probeKey.Value))
			{
				ProbeNearby();
			}
			if (Input.GetKeyDown(_recordKey.Value))
			{
				Home home = NearestOwnedHome();
				if (home == null)
				{
					Log.LogInfo((object)"[persist] record: no owned tavern nearby.");
				}
				else
				{
					int num = CaptureItemsFor(home);
					ModSave.Save(((BaseUnityPlugin)this).Info, (object)_save);
					Log.LogInfo((object)$"[persist] recorded {num} item(s) for '{home.def.id}'.");
				}
			}
			if (Input.GetKeyDown(_restoreKey.Value))
			{
				Home home2 = NearestOwnedHome();
				if (home2 == null)
				{
					Log.LogInfo((object)"[persist] restore: no owned tavern nearby.");
				}
				else
				{
					int num2 = RestoreItemsFor(home2);
					Log.LogInfo((object)$"[persist] restored {num2} item(s) for '{home2.def.id}'.");
				}
			}
			if (_worldReady && _persistItems.Value)
			{
				UpdateHomePresence();
			}
			if (!_worldReady && _enableTavernClaim.Value && OriginReady && GameState.playing && !GameState.currentlyLoading && (Object)(object)Utilities.PlayerTransform != (Object)null)
			{
				_watchdogTimer += Time.deltaTime;
				if (_watchdogTimer >= 5f && Object.FindObjectsOfType<Tavern>().Length != 0)
				{
					Log.LogWarning((object)"[watchdog] save-load event never reached PlayerHome — self-triggering world setup.");
					OnWorldReady();
				}
			}
			if (_worldReady && _enableTavernClaim.Value)
			{
				_claimScanTimer += Time.deltaTime;
				if (_claimScanTimer >= 2f)
				{
					_claimScanTimer = 0f;
					SetupTavernClaims();
					SweepOwnedTavernFurniture();
				}
			}
		}

		private void OnWorldReady()
		{
			try
			{
				if (_dumpPrefabs.Value)
				{
					DumpPrefabs();
					return;
				}
				foreach (Home home in _homes)
				{
					if ((Object)(object)home.root != (Object)null && !home.isClaimedTavern)
					{
						Object.Destroy((Object)(object)home.root);
					}
				}
				_homes.Clear();
				SaveData saveData = default(SaveData);
				if (ModSave.Load<SaveData>(((BaseUnityPlugin)this).Info, ref saveData) && saveData != null)
				{
					_save = saveData;
				}
				else
				{
					_save = new SaveData();
				}
				SpawnPlaceableHomes();
				Log.LogInfo((object)$"TAVERNDIAG: OnWorldReady — EnableTavernClaim = {_enableTavernClaim.Value}");
				if (_enableTavernClaim.Value)
				{
					SetupTavernClaims();
				}
				RestoreWarehouses();
				_worldReady = true;
				_claimScanTimer = 0f;
			}
			catch (Exception ex)
			{
				Log.LogError((object)("Setup failed: " + ex));
			}
		}

		private void SpawnPlaceableHomes()
		{
			//IL_0065: Unknown result type (might be due to invalid IL or missing references)
			//IL_006a: 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_0083: Expected O, but got Unknown
			//IL_008a: Unknown result type (might be due to invalid IL or missing references)
			//IL_008b: 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_0174: Unknown result type (might be due to invalid IL or missing references)
			//IL_0175: Unknown result type (might be due to invalid IL or missing references)
			//IL_01be: 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)
			//IL_00ca: Unknown result type (might be due to invalid IL or missing references)
			//IL_01e3: Unknown result type (might be due to invalid IL or missing references)
			//IL_014b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0150: Unknown result type (might be due to invalid IL or missing references)
			List<HomeDef> list = LoadHomesConfig();
			Vector3 val = default(Vector3);
			foreach (HomeDef item in list)
			{
				if (string.IsNullOrEmpty(item.id))
				{
					Log.LogWarning((object)"A home in homes.txt has no id; skipped.");
					continue;
				}
				((Vector3)(ref val))..ctor(item.x, item.y, item.z);
				Quaternion val2 = Quaternion.Euler(0f, item.yaw, 0f);
				GameObject val3 = new GameObject("PlayerHome_" + item.id);
				val3.transform.SetPositionAndRotation(RealToWorld(val), val2);
				if (!string.IsNullOrEmpty(item.prefab))
				{
					GameObject prefabGameObject = GameAssets.GetPrefabGameObject(item.prefab);
					if ((Object)(object)prefabGameObject != (Object)null)
					{
						StripBusiness(Object.Instantiate<GameObject>(prefabGameObject, val3.transform.position, val2, val3.transform));
					}
					else
					{
						Log.LogWarning((object)("[" + item.id + "] prefab '" + item.prefab + "' not found (use DumpPrefabs)."));
					}
				}
				if (!string.IsNullOrEmpty(item.bedPrefab))
				{
					GameObject prefabGameObject2 = GameAssets.GetPrefabGameObject(item.bedPrefab);
					if ((Object)(object)prefabGameObject2 != (Object)null)
					{
						Object.Instantiate<GameObject>(prefabGameObject2, val3.transform.position, val2, val3.transform);
					}
				}
				Home home = new Home
				{
					def = item,
					root = val3,
					realPos = val,
					owned = _save.ownedIds.Contains(item.id),
					isClaimedTavern = false
				};
				_homes.Add(home);
				MakeRestButton(home, new Vector3(0.8f, 1.3f, 0f));
				if (!home.owned)
				{
					MakeBuyButton(home, new Vector3(-0.8f, 1.3f, 0f));
				}
			}
			Log.LogInfo((object)$"Spawned {list.Count} placeable home(s).");
		}

		private void SetupTavernClaims()
		{
			//IL_0072: Unknown result type (might be due to invalid IL or missing references)
			//IL_0077: Unknown result type (might be due to invalid IL or missing references)
			//IL_007c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0085: Unknown result type (might be due to invalid IL or missing references)
			//IL_0096: Unknown result type (might be due to invalid IL or missing references)
			//IL_031f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0326: Unknown result type (might be due to invalid IL or missing references)
			//IL_032b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0330: Unknown result type (might be due to invalid IL or missing references)
			//IL_03f2: Unknown result type (might be due to invalid IL or missing references)
			//IL_03f7: Unknown result type (might be due to invalid IL or missing references)
			//IL_0401: Unknown result type (might be due to invalid IL or missing references)
			//IL_0406: Unknown result type (might be due to invalid IL or missing references)
			//IL_0423: Unknown result type (might be due to invalid IL or missing references)
			//IL_03c3: Unknown result type (might be due to invalid IL or missing references)
			//IL_0478: Unknown result type (might be due to invalid IL or missing references)
			//IL_047d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0498: Unknown result type (might be due to invalid IL or missing references)
			//IL_048c: Unknown result type (might be due to invalid IL or missing references)
			//IL_049d: Unknown result type (might be due to invalid IL or missing references)
			//IL_04ba: Unknown result type (might be due to invalid IL or missing references)
			//IL_04c1: Unknown result type (might be due to invalid IL or missing references)
			//IL_04d2: Unknown result type (might be due to invalid IL or missing references)
			//IL_04d4: Unknown result type (might be due to invalid IL or missing references)
			int num = 0;
			Tavern[] array = Object.FindObjectsOfType<Tavern>();
			int num2 = Resources.FindObjectsOfTypeAll<Tavern>().Length;
			if (array.Length != _lastSeenTavernCount)
			{
				_lastSeenTavernCount = array.Length;
				Log.LogInfo((object)$"TAVERNDIAG: active Tavern objects = {array.Length}, incl-inactive = {num2}");
			}
			Tavern[] array2 = array;
			foreach (Tavern tav in array2)
			{
				Transform transform = ((Component)tav).transform;
				Vector3 val = WorldToReal(transform.position);
				string id = $"tavern@{Mathf.Round(val.x)}_{Mathf.Round(val.z)}";
				if (_excludedIds.Contains(id))
				{
					continue;
				}
				Home home = _homes.Find((Home h) => h.isClaimedTavern && ((Object)(object)h.tavern == (Object)(object)tav || h.def.id == id));
				if (home == null)
				{
					bool confident;
					int price = PriceForTavern(tav, id, out confident);
					home = new Home
					{
						def = new HomeDef
						{
							id = id,
							price = price,
							storeRadius = (((Object)(object)I != (Object)null && I._furnishRadius != null) ? I._furnishRadius.Value : 14f)
						},
						root = ((Component)tav).gameObject,
						owned = _save.ownedIds.Contains(id),
						isClaimedTavern = true,
						tavern = tav,
						priceConfirmed = confident
					};
					_homes.Add(home);
					if (home.owned)
					{
						DisableTavernBusiness(home);
					}
					num++;
				}
				else if ((Object)(object)home.tavern != (Object)(object)tav)
				{
					home.tavern = tav;
					home.root = ((Component)tav).gameObject;
					home.buttonAnchored = false;
					if (home.owned)
					{
						DisableTavernBusiness(home);
					}
					if (home.owned && _persistItems != null && _persistItems.Value)
					{
						int num3 = RestoreItemsFor(home);
						if (num3 > 0)
						{
							Log.LogInfo((object)$"[persist] '{home.def.id}' streamed in: restored {num3} item(s).");
						}
					}
				}
				if (!home.owned && !home.priceConfirmed)
				{
					bool confident2;
					int num4 = PriceForTavern(tav, id, out confident2);
					if (confident2)
					{
						home.def.price = num4;
						home.priceConfirmed = true;
						RefreshBuyLabel(home);
						Log.LogInfo((object)$"[price] {id} re-resolved -> {num4}g (town NPC now ready)");
					}
				}
				if (home.owned || home.buttonAnchored)
				{
					continue;
				}
				GPButtonTavernSleep val2 = null;
				float num5 = float.MaxValue;
				GPButtonTavernSleep[] array3 = Object.FindObjectsOfType<GPButtonTavernSleep>();
				foreach (GPButtonTavernSleep val3 in array3)
				{
					Vector3 val4 = ((Component)val3).transform.position - transform.position;
					float sqrMagnitude = ((Vector3)(ref val4)).sqrMagnitude;
					if (sqrMagnitude < num5)
					{
						num5 = sqrMagnitude;
						val2 = val3;
					}
				}
				if ((Object)(object)val2 == (Object)null)
				{
					if (array.Length == _lastSeenTavernCount)
					{
					}
				}
				else
				{
					if (num5 > 900f)
					{
						continue;
					}
					if (home.owned)
					{
						if (Object.op_Implicit((Object)(object)home.buyButton))
						{
							((Component)home.buyButton).gameObject.SetActive(false);
						}
						continue;
					}
					if ((Object)(object)home.buyButton == (Object)null)
					{
						MakeBuyButton(home, Vector3.zero);
					}
					((Component)home.buyButton).gameObject.SetActive(true);
					((Component)home.buyButton).transform.position = ((Component)val2).transform.position - Vector3.up * 0.35f;
					((Component)home.buyButton).transform.rotation = ((Component)val2).transform.rotation;
					((GoPointerButton)home.buyButton).lookText = (home.buyLabelAdded ? "" : $"buy tavern ({home.def.price}g)");
					home.buttonAnchored = true;
					Vector3 position = ((Component)home.buyButton).transform.position;
					Vector3 val5 = (((Object)(object)Utilities.PlayerTransform != (Object)null) ? Utilities.PlayerTransform.position : Vector3.zero);
					Log.LogInfo((object)($"TAVERNDIAG: anchored '{((Object)((Component)tav).gameObject).name}' | buttonPos={position} | playerPos={val5} " + $"| dist={Vector3.Distance(position, val5):F1} | sleepDist={Mathf.Sqrt(num5):F1}"));
				}
			}
			if (_homes.Count != _lastTrackedCount)
			{
				Log.LogInfo((object)$"[experimental] {_homes.Count} tavern home(s) tracked.");
				_lastTrackedCount = _homes.Count;
			}
		}

		private int ButtonLayer()
		{
			if (_btnLayer >= 0)
			{
				return _btnLayer;
			}
			GoPointerButton val = Object.FindObjectOfType<GoPointerButton>();
			_btnLayer = (((Object)(object)val != (Object)null) ? ((Component)val).gameObject.layer : 0);
			return _btnLayer;
		}

		private GameObject MakeButtonPlate(Home home, Vector3 offset, string name)
		{
			//IL_002a: 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_0056: 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_0068: Unknown result type (might be due to invalid IL or missing references)
			//IL_0079: Unknown result type (might be due to invalid IL or missing references)
			//IL_0084: 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_00a0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fa: Unknown result type (might be due to invalid IL or missing references)
			GameObject obj = GameObject.CreatePrimitive((PrimitiveType)3);
			((Object)obj).name = name;
			obj.transform.SetParent(home.root.transform, false);
			obj.transform.localPosition = offset;
			Vector3 val = default(Vector3);
			((Vector3)(ref val))..ctor(0.5f, 0.25f, 0.04f);
			Vector3 lossyScale = home.root.transform.lossyScale;
			obj.transform.localScale = new Vector3(val.x / Mathf.Max(0.0001f, Mathf.Abs(lossyScale.x)), val.y / Mathf.Max(0.0001f, Mathf.Abs(lossyScale.y)), val.z / Mathf.Max(0.0001f, Mathf.Abs(lossyScale.z)));
			obj.layer = ButtonLayer();
			MeshRenderer component = obj.GetComponent<MeshRenderer>();
			if ((Object)(object)component != (Object)null && (Object)(object)((Renderer)component).material != (Object)null)
			{
				((Renderer)component).material.color = new Color(0.88f, 0.8f, 0.62f);
			}
			return obj;
		}

		private void MakeRestButton(Home home, Vector3 offset)
		{
			//IL_0002: Unknown result type (might be due to invalid IL or missing references)
			HomeRestButton homeRestButton = MakeButtonPlate(home, offset, "RestButton").AddComponent<HomeRestButton>();
			homeRestButton.home = home;
			((GoPointerButton)homeRestButton).lookText = "rest until morning";
			((GoPointerButton)homeRestButton).unclickable = !home.owned;
			home.restButton = homeRestButton;
		}

		private void MakeBuyButton(Home home, Vector3 offset)
		{
			//IL_0002: Unknown result type (might be due to invalid IL or missing references)
			GameObject val = MakeButtonPlate(home, offset, "BuyButton");
			HomeBuyButton homeBuyButton = val.AddComponent<HomeBuyButton>();
			homeBuyButton.home = home;
			((GoPointerButton)homeBuyButton).lookText = $"buy this home ({home.def.price} gold)";
			home.buyButton = homeBuyButton;
			AddBuyLabel(home, val);
		}

		private void AddBuyLabel(Home home, GameObject plate)
		{
			//IL_00ab: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d5: 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)
			//IL_00f9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fe: Unknown result type (might be due to invalid IL or missing references)
			//IL_0100: Unknown result type (might be due to invalid IL or missing references)
			//IL_010b: 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_012f: Unknown result type (might be due to invalid IL or missing references)
			//IL_013c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0153: Unknown result type (might be due to invalid IL or missing references)
			//IL_0160: Unknown result type (might be due to invalid IL or missing references)
			//IL_0172: Unknown result type (might be due to invalid IL or missing references)
			//IL_017c: 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_019b: Unknown result type (might be due to invalid IL or missing references)
			//IL_01a1: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ab: Unknown result type (might be due to invalid IL or missing references)
			//IL_01f7: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				if ((Object)(object)home.tavern == (Object)null)
				{
					return;
				}
				Component value = Traverse.Create((object)home.tavern).Field("text").GetValue<Component>();
				if ((Object)(object)value == (Object)null)
				{
					return;
				}
				Type type = ((object)value).GetType();
				object value2 = Traverse.Create((object)value).Property("font", (object[])null).GetValue();
				MeshRenderer component = value.GetComponent<MeshRenderer>();
				Material val = (((Object)(object)component != (Object)null) ? ((Renderer)component).sharedMaterial : null);
				float num = Traverse.Create((object)value).Property("characterSize", (object[])null).GetValue<float>();
				int num2 = Traverse.Create((object)value).Property("fontSize", (object[])null).GetValue<int>();
				Vector3 lossyScale = value.transform.lossyScale;
				if (num <= 0f)
				{
					num = 0.1f;
				}
				if (num2 <= 0)
				{
					num2 = 30;
				}
				GameObject val2 = new GameObject("BuyLabel")
				{
					layer = plate.layer
				};
				val2.transform.SetParent(plate.transform, false);
				Vector3 lossyScale2 = plate.transform.lossyScale;
				val2.transform.localScale = new Vector3(1.3f * lossyScale.x / Mathf.Max(0.0001f, Mathf.Abs(lossyScale2.x)), 1.3f * lossyScale.y / Mathf.Max(0.0001f, Mathf.Abs(lossyScale2.y)), 1.3f * lossyScale.z / Mathf.Max(0.0001f, Mathf.Abs(lossyScale2.z)));
				val2.transform.localPosition = new Vector3(0f, 0f, -0.55f);
				val2.transform.localRotation = Quaternion.identity;
				Component val3 = val2.AddComponent(type);
				Traverse val4 = Traverse.Create((object)val3);
				val4.Property("text", (object[])null).SetValue((object)$"Buy Tavern\n{home.def.price}g");
				val4.Property("color", (object[])null).SetValue((object)Color.black);
				val4.Property("characterSize", (object[])null).SetValue((object)num);
				val4.Property("fontSize", (object[])null).SetValue((object)num2);
				val4.Property("lineSpacing", (object[])null).SetValue((object)0.8f);
				if (value2 != null)
				{
					val4.Property("font", (object[])null).SetValue(value2);
				}
				SetEnumProp(val3, "anchor", 4);
				SetEnumProp(val3, "alignment", 1);
				MeshRenderer component2 = val2.GetComponent<MeshRenderer>();
				if ((Object)(object)component2 != (Object)null)
				{
					if ((Object)(object)val != (Object)null)
					{
						((Renderer)component2).sharedMaterial = val;
					}
					((Renderer)component2).shadowCastingMode = (ShadowCastingMode)0;
					((Renderer)component2).receiveShadows = false;
				}
				home.buyLabelTM = val3;
				home.buyLabelAdded = true;
			}
			catch (Exception ex)
			{
				Log.LogWarning((object)("[buy-label] couldn't add permanent label: " + ex.Message));
			}
		}

		private void RefreshBuyLabel(Home home)
		{
			try
			{
				if (home != null && !((Object)(object)home.buyLabelTM == (Object)null))
				{
					Traverse.Create((object)home.buyLabelTM).Property("text", (object[])null).SetValue((object)$"Buy Tavern\n{home.def.price}g");
				}
			}
			catch
			{
			}
		}

		private void SetEnumProp(object obj, string prop, int val)
		{
			try
			{
				PropertyInfo property = obj.GetType().GetProperty(prop);
				if (property != null)
				{
					property.SetValue(obj, Enum.ToObject(property.PropertyType, val), null);
				}
			}
			catch
			{
			}
		}

		internal void TryBuy(Home home)
		{
			if (home == null || home.owned)
			{
				return;
			}
			int num = 3;
			if (PlayerGold.currency == null || num < 0 || num >= PlayerGold.currency.Length)
			{
				Log.LogWarning((object)$"Bad currency index {num}; cannot charge.");
				Notify("Couldn't process the purchase here.");
			}
			else if (PlayerGold.currency[num] >= home.def.price)
			{
				PlayerGold.currency[num] -= home.def.price;
				home.owned = true;
				if (!_save.ownedIds.Contains(home.def.id))
				{
					_save.ownedIds.Add(home.def.id);
				}
				if (home.isClaimedTavern)
				{
					DisableTavernBusiness(home);
				}
				if (Object.op_Implicit((Object)(object)home.buyButton))
				{
					((Component)home.buyButton).gameObject.SetActive(false);
				}
				if (Object.op_Implicit((Object)(object)home.restButton))
				{
					((GoPointerButton)home.restButton).unclickable = false;
				}
				Notify(home.isClaimedTavern ? "Tavern claimed!" : "Home purchased!");
				ModSave.Save(((BaseUnityPlugin)this).Info, (object)_save);
			}
			else
			{
				Notify("Not enough money.");
			}
		}

		private string GetPortName(Transform t)
		{
			Transform val = t;
			while ((Object)(object)val != (Object)null)
			{
				string name = ((Object)val).name;
				int num = name.IndexOf('(');
				int num2 = name.IndexOf(')');
				if (num >= 0 && num2 > num + 1)
				{
					return name.Substring(num + 1, num2 - num - 1).Trim().ToLowerInvariant();
				}
				val = val.parent;
			}
			return "";
		}

		private string GetTownName(Tavern tav)
		{
			//IL_0013: Unknown result type (might be due to invalid IL or missing references)
			//IL_0018: Unknown result type (might be due to invalid IL or missing references)
			//IL_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_0034: Unknown result type (might be due to invalid IL or missing references)
			//IL_0039: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				TavernRumorsDude[] array = Object.FindObjectsOfType<TavernRumorsDude>();
				TavernRumorsDude val = null;
				float num = 625f;
				Vector3 position = ((Component)tav).transform.position;
				TavernRumorsDude[] array2 = array;
				foreach (TavernRumorsDude val2 in array2)
				{
					Vector3 val3 = ((Component)val2).transform.position - position;
					float sqrMagnitude = ((Vector3)(ref val3)).sqrMagnitude;
					if (sqrMagnitude < num)
					{
						num = sqrMagnitude;
						val = val2;
					}
				}
				if ((Object)(object)val == (Object)null)
				{
					return "";
				}
				int portIndex = val.portIndex;
				Port[] ports = Port.ports;
				if (ports == null || portIndex < 0 || portIndex >= ports.Length || (Object)(object)ports[portIndex] == (Object)null)
				{
					return "";
				}
				string value = Traverse.Create((object)ports[portIndex]).Field("portName").GetValue<string>();
				return (value != null) ? value.Trim().ToLowerInvariant() : "";
			}
			catch
			{
				return "";
			}
		}

		private int PriceForTavern(Tavern tav, string id, out bool confident)
		{
			confident = true;
			string portName = GetPortName(((Component)tav).transform);
			string townName = GetTownName(tav);
			if (_priceOverrideMap.TryGetValue(id.ToLowerInvariant(), out var value))
			{
				Log.LogInfo((object)$"[price] {id} town='{townName}' -> {value}g (config id override)");
				return value;
			}
			foreach (KeyValuePair<string, int> item in _priceOverrideMap)
			{
				if (item.Key.Length > 0 && (townName.Contains(item.Key) || portName.Contains(item.Key)))
				{
					Log.LogInfo((object)$"[price] {id} town='{townName}' -> {item.Value}g (config name override '{item.Key}')");
					return item.Value;
				}
			}
			if (PriceById.TryGetValue(id, out var value2))
			{
				Log.LogInfo((object)$"[price] {id} town='{townName}' -> {value2}g (pinned by id)");
				return value2;
			}
			if (townName.Length > 0)
			{
				foreach (KeyValuePair<string, int> item2 in PriceByTown)
				{
					if (townName.Contains(item2.Key))
					{
						Log.LogInfo((object)$"[price] {id} town='{townName}' -> {item2.Value}g (town '{item2.Key}')");
						return item2.Value;
					}
				}
			}
			confident = townName.Length > 0;
			foreach (KeyValuePair<string, int> item3 in PriceTable)
			{
				if (portName.Contains(item3.Key))
				{
					Log.LogWarning((object)string.Format("[price] {0} town='{1}' port='{2}' -> {3}g (island-name fallback '{4}'{5})", id, townName, portName, item3.Value, item3.Key, confident ? "" : "; town NPC not ready — will re-resolve"));
					return item3.Value;
				}
			}
			Log.LogWarning((object)string.Format("[price] {0} town='{1}' port='{2}' -> {3}g (DEFAULT{4})", id, townName, portName, 75, confident ? " — town has no price-table entry" : "; town NPC not ready — will re-resolve"));
			return 75;
		}

		internal void Rest(Home home)
		{
			if (home == null)
			{
				return;
			}
			if (!home.owned)
			{
				Notify("You don't own this home yet.");
			}
			else if (!Object.op_Implicit((Object)(object)GameState.inBed) && !GameState.sleeping)
			{
				if ((Object)(object)home.tavern != (Object)null)
				{
					home.tavern.ClickSleepButton();
				}
				else
				{
					Sleep.instance.FallAsleep();
				}
			}
		}

		internal bool OwnsTavern(Tavern tav)
		{
			foreach (Home home in _homes)
			{
				if (home.owned && home.isClaimedTavern && (Object)(object)home.tavern == (Object)(object)tav)
				{
					return true;
				}
			}
			return false;
		}

		internal bool SleepingAtOwnedTavern()
		{
			//IL_0014: Unknown result type (might be due to invalid IL or missing references)
			//IL_0019: 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_005a: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)Utilities.PlayerTransform == (Object)null)
			{
				return false;
			}
			Vector3 position = Utilities.PlayerTransform.position;
			foreach (Home home in _homes)
			{
				if (home.owned && home.isClaimedTavern && !((Object)(object)home.tavern == (Object)null) && Vector3.Distance(position, ((Component)home.tavern).transform.position) <= 60f)
				{
					return true;
				}
			}
			return false;
		}

		internal static string SleepSnapshot()
		{
			return $"food={PlayerNeeds.food:F3} water={PlayerNeeds.water:F3} sleep={PlayerNeeds.sleep:F3} " + $"foodDebt={PlayerNeeds.foodDebt:F3} sleepDebt={PlayerNeeds.sleepDebt:F3} | " + $"sleeping={GameState.sleeping} inBed={GameState.inBed} sleepingInTavern={GameState.sleepingInTavern}";
		}

		internal void StartSleepTrace(string trigger)
		{
			if (TraceSleep)
			{
				Log.LogInfo((object)("[sleep-trace] === REST TRIGGERED via " + trigger + " ===  " + SleepSnapshot()));
				((MonoBehaviour)this).StartCoroutine(SleepTraceCo());
			}
		}

		private IEnumerator SleepTraceCo()
		{
			int frame = 0;
			float w = 0f;
			while (!GameState.sleeping && w < 1f)
			{
				w += Time.deltaTime;
				yield return null;
			}
			while (GameState.sleeping)
			{
				Log.LogInfo((object)$"[sleep-trace] frame {frame++}: {SleepSnapshot()}");
				yield return null;
			}
			for (int i = 0; i < 30; i++)
			{
				Log.LogInfo((object)$"[sleep-trace] post-wake +{i}: {SleepSnapshot()}");
				yield return null;
			}
			Log.LogInfo((object)("[sleep-trace] === TRACE END ===  " + SleepSnapshot()));
		}

		private Home NearestOwnedHome()
		{
			//IL_0014: Unknown result type (might be due to invalid IL or missing references)
			//IL_0019: 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_0049: Unknown result type (might be due to invalid IL or missing references)
			//IL_004b: Unknown result type (might be due to invalid IL or missing references)
			//IL_004c: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)Utilities.PlayerTransform == (Object)null)
			{
				return null;
			}
			Vector3 position = Utilities.PlayerTransform.position;
			Home result = null;
			float num = float.MaxValue;
			foreach (Home home in _homes)
			{
				if (home.owned)
				{
					Vector3 val = HomeAnchor(home);
					float num2 = Vector3.Distance(position, val);
					if (num2 < num)
					{
						num = num2;
						result = home;
					}
				}
			}
			return result;
		}

		private static Vector3 HomeAnchor(Home home)
		{
			//IL_0019: Unknown result type (might be due to invalid IL or missing references)
			//IL_003e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0038: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)home.root != (Object)null)
			{
				return home.root.transform.position;
			}
			if ((Object)(object)home.tavern != (Object)null)
			{
				return ((Component)home.tavern).transform.position;
			}
			return Vector3.zero;
		}

		private static HashSet<long> LiveInstanceIds()
		{
			HashSet<long> hashSet = new HashSet<long>();
			SaveablePrefab[] array = Object.FindObjectsOfType<SaveablePrefab>();
			foreach (SaveablePrefab val in array)
			{
				if ((Object)(object)val == (Object)null)
				{
					continue;
				}
				try
				{
					object value = Traverse.Create((object)val).Field("instanceId").GetValue();
					if (value != null)
					{
						hashSet.Add(Convert.ToInt64(value));
					}
				}
				catch
				{
				}
			}
			return hashSet;
		}

		private int CaptureItemsFor(Home home)
		{
			//IL_0060: Unknown result type (might be due to invalid IL or missing references)
			//IL_0065: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fb: Unknown result type (might be due to invalid IL or missing references)
			//IL_0100: Unknown result type (might be due to invalid IL or missing references)
			Warehouse warehouse = _save.warehouses.Find((Warehouse w) => w.homeId == home.def.id);
			if (warehouse == null)
			{
				warehouse = new Warehouse
				{
					homeId = home.def.id
				};
				_save.warehouses.Add(warehouse);
			}
			Vector3 val = HomeAnchor(home);
			List<StoredItem> list = new List<StoredItem>();
			SaveablePrefab[] array = Object.FindObjectsOfType<SaveablePrefab>();
			foreach (SaveablePrefab val2 in array)
			{
				if ((Object)(object)val2 == (Object)null)
				{
					continue;
				}
				try
				{
					if (Traverse.Create((object)val2).Method("GetParentObject", Array.Empty<object>()).GetValue<int>() > 0)
					{
						continue;
					}
					object value = Traverse.Create((object)val2).Field("item").GetValue();
					if (value == null || !Convert.ToBoolean(Traverse.Create(value).Field("sold").GetValue()) || Vector3.Distance(((Component)val2).transform.position, val) > home.def.storeRadius)
					{
						continue;
					}
					SavePrefabData value2 = Traverse.Create((object)val2).Method("PrepareSaveData", Array.Empty<object>()).GetValue<SavePrefabData>();
					if (value2 != null)
					{
						string blob;
						using (MemoryStream memoryStream = new MemoryStream())
						{
							new BinaryFormatter().Serialize(memoryStream, value2);
							blob = Convert.ToBase64String(memoryStream.ToArray());
						}
						list.Add(new StoredItem
						{
							prefab = CleanName(((Object)((Component)val2).gameObject).name),
							blob = blob
						});
					}
				}
				catch (Exception ex)
				{
					Log.LogWarning((object)("[persist] capture failed for '" + ((Object)((Component)val2).gameObject).name + "': " + ex.Message));
				}
			}
			if (list.Count == 0 && warehouse.items.Count > 0)
			{
				Log.LogInfo((object)$"[persist] capture for '{home.def.id}' found 0 items but {warehouse.items.Count} are stored — keeping the stored set (guard).");
				return warehouse.items.Count;
			}
			warehouse.items = list;
			return list.Count;
		}

		private int RestoreItemsFor(Home home)
		{
			//IL_00cf: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d6: Expected O, but got Unknown
			Warehouse warehouse = _save.warehouses.Find((Warehouse w) => w.homeId == home.def.id);
			if (warehouse == null || warehouse.items.Count == 0)
			{
				return 0;
			}
			HashSet<long> hashSet = LiveInstanceIds();
			PrefabsDirectory val = Object.FindObjectOfType<PrefabsDirectory>();
			GameObject[] array = (((Object)(object)val != (Object)null) ? Traverse.Create((object)val).Field("directory").GetValue<GameObject[]>() : null);
			if (array == null)
			{
				Log.LogWarning((object)"[persist] restore: prefab directory unavailable.");
				return 0;
			}
			int num = 0;
			foreach (StoredItem item2 in warehouse.items)
			{
				if (string.IsNullOrEmpty(item2.blob))
				{
					continue;
				}
				try
				{
					SavePrefabData val2;
					using (MemoryStream serializationStream = new MemoryStream(Convert.FromBase64String(item2.blob)))
					{
						val2 = (SavePrefabData)new BinaryFormatter().Deserialize(serializationStream);
					}
					if (val2 == null)
					{
						continue;
					}
					long item = Convert.ToInt64(val2.instanceId);
					if (hashSet.Contains(item))
					{
						continue;
					}
					int prefabIndex = val2.prefabIndex;
					if (prefabIndex < 0 || prefabIndex >= array.Length || (Object)(object)array[prefabIndex] == (Object)null)
					{
						Log.LogWarning((object)$"[persist] restore: bad prefab index {prefabIndex} for '{item2.prefab}'.");
						continue;
					}
					GameObject val3 = Object.Instantiate<GameObject>(array[prefabIndex]);
					SaveablePrefab component = val3.GetComponent<SaveablePrefab>();
					if ((Object)(object)component == (Object)null)
					{
						Object.Destroy((Object)(object)val3);
						continue;
					}
					Traverse.Create((object)component).Method("Load", new object[1] { val2 }).GetValue();
					hashSet.Add(item);
					num++;
				}
				catch (Exception ex)
				{
					Log.LogWarning((object)("[persist] restore failed for '" + item2.prefab + "': " + ex.Message));
				}
			}
			return num;
		}

		private void OnGameSave()
		{
			if (!_persistItems.Value)
			{
				return;
			}
			try
			{
				foreach (Home home in _homes)
				{
					if (home.owned && home.playerInside)
					{
						CaptureItemsFor(home);
					}
				}
				ModSave.Save(((BaseUnityPlugin)this).Info, (object)_save);
			}
			catch (Exception ex)
			{
				Log.LogWarning((object)("[persist] OnGameSave failed: " + ex.Message));
			}
		}

		private void RestoreWarehouses()
		{
			foreach (Home home in _homes)
			{
				home.playerInside = false;
			}
		}

		private void UpdateHomePresence()
		{
			//IL_0013: Unknown result type (might be due to invalid IL or missing references)
			//IL_0018: Unknown result type (might be due to invalid IL or missing references)
			//IL_0069: Unknown result type (might be due to invalid IL or missing references)
			//IL_006b: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)Utilities.PlayerTransform == (Object)null)
			{
				return;
			}
			Vector3 position = Utilities.PlayerTransform.position;
			foreach (Home home in _homes)
			{
				if (!home.owned)
				{
					continue;
				}
				if (home.isClaimedTavern)
				{
					if ((Object)(object)home.tavern == (Object)null)
					{
						continue;
					}
				}
				else if ((Object)(object)home.root == (Object)null)
				{
					continue;
				}
				bool flag = Vector3.Distance(position, HomeAnchor(home)) <= home.def.storeRadius;
				if (flag && !home.playerInside)
				{
					int num = RestoreItemsFor(home);
					if (num > 0)
					{
						Log.LogInfo((object)$"[persist] entered '{home.def.id}': restored {num} item(s).");
					}
				}
				else if (!flag && home.playerInside)
				{
					int num2 = CaptureItemsFor(home);
					ModSave.Save(((BaseUnityPlugin)this).Info, (object)_save);
					Log.LogInfo((object)$"[persist] left '{home.def.id}': captured {num2} item(s).");
				}
				home.playerInside = flag;
			}
		}

		private void StripBusiness(GameObject building)
		{
			Tavern[] componentsInChildren = building.GetComponentsInChildren<Tavern>(true);
			for (int i = 0; i < componentsInChildren.Length; i++)
			{
				Object.Destroy((Object)(object)componentsInChildren[i]);
			}
			GPButtonTavernSleep[] componentsInChildren2 = building.GetComponentsInChildren<GPButtonTavernSleep>(true);
			for (int i = 0; i < componentsInChildren2.Length; i++)
			{
				Object.Destroy((Object)(object)((Component)componentsInChildren2[i]).gameObject);
			}
			Shopkeeper[] componentsInChildren3 = building.GetComponentsInChildren<Shopkeeper>(true);
			for (int i = 0; i < componentsInChildren3.Length; i++)
			{
				Object.Destroy((Object)(object)((Component)componentsInChildren3[i]).gameObject);
			}
			ShopItemSpawner[] componentsInChildren4 = building.GetComponentsInChildren<ShopItemSpawner>(true);
			for (int i = 0; i < componentsInChildren4.Length; i++)
			{
				Object.Destroy((Object)(object)((Component)componentsInChildren4[i]).gameObject);
			}
		}

		private static bool IsFurnitureName(string nm)
		{
			string[] furnitureHardExclude = FurnitureHardExclude;
			foreach (string value in furnitureHardExclude)
			{
				if (nm.IndexOf(value, StringComparison.OrdinalIgnoreCase) >= 0)
				{
					return false;
				}
			}
			furnitureHardExclude = FurnitureForceInclude;
			foreach (string value2 in furnitureHardExclude)
			{
				if (nm.IndexOf(value2, StringComparison.OrdinalIgnoreCase) >= 0)
				{
					return true;
				}
			}
			furnitureHardExclude = FurnitureExclude;
			foreach (string value3 in furnitureHardExclude)
			{
				if (nm.IndexOf(value3, StringComparison.OrdinalIgnoreCase) >= 0)
				{
					return false;
				}
			}
			furnitureHardExclude = FurnitureKeywords;
			foreach (string value4 in furnitureHardExclude)
			{
				if (nm.IndexOf(value4, StringComparison.OrdinalIgnoreCase) >= 0)
				{
					return true;
				}
			}
			return false;
		}

		private void ParseClearOverrides(string raw)
		{
			_clearRadiusOverrides.Clear();
			if (string.IsNullOrEmpty(raw))
			{
				return;
			}
			string[] array = raw.Split(new char[1] { ';' });
			for (int i = 0; i < array.Length; i++)
			{
				string text = array[i].Trim();
				if (text.Length == 0)
				{
					continue;
				}
				int num = text.LastIndexOf(':');
				if (num > 0 && num < text.Length - 1)
				{
					string key = text.Substring(0, num).Trim();
					if (float.TryParse(text.Substring(num + 1).Trim(), NumberStyles.Float, CultureInfo.InvariantCulture, out var result) && result > 0f)
					{
						_clearRadiusOverrides[key] = result;
					}
				}
			}
		}

		private float ClearRadiusFor(Home home)
		{
			if (home != null && home.def != null && _clearRadiusOverrides.TryGetValue(home.def.id, out var value))
			{
				return value;
			}
			if (home != null && home.def != null && _clearRadiusDefaults.TryGetValue(home.def.id, out var value2))
			{
				return value2;
			}
			return CLEAR_RADIUS;
		}

		private int RevertCombinersNear(Vector3 anchor, bool verbose, float radius = -1f)
		{
			//IL_00c8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00cd: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ce: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d3: Unknown result type (might be due to invalid IL or missing references)
			int num = 0;
			float num2 = ((radius > 0f) ? radius : CLEAR_RADIUS);
			float num3 = (num2 + 5f) * (num2 + 5f);
			MonoBehaviour[] array = Resources.FindObjectsOfTypeAll<MonoBehaviour>();
			foreach (MonoBehaviour val in array)
			{
				if ((Object)(object)val == (Object)null || ((object)val).GetType().Name != "MeshCombiner" || (Object)(object)((Component)val).transform == (Object)null)
				{
					continue;
				}
				GameObject[] array2 = null;
				try
				{
					array2 = Traverse.Create((object)val).Field("combinedObjects").GetValue<GameObject[]>();
				}
				catch
				{
				}
				if (array2 == null || array2.Length == 0)
				{
					continue;
				}
				float num4 = float.MaxValue;
				GameObject[] array3 = array2;
				foreach (GameObject val2 in array3)
				{
					if (!((Object)(object)val2 == (Object)null))
					{
						Vector3 val3 = val2.transform.position - anchor;
						float sqrMagnitude = ((Vector3)(ref val3)).sqrMagnitude;
						if (sqrMagnitude < num4)
						{
							num4 = sqrMagnitude;
						}
					}
				}
				if (verbose)
				{
					Log.LogInfo((object)$"[furn-scan] '{((Object)((Component)val).gameObject).name}' baked={array2.Length} nearest={((num4 == float.MaxValue) ? (-1f) : Mathf.Sqrt(num4)):F1}u (threshold {CLEAR_RADIUS + 5f:F0}u)");
				}
				if (!(num4 > num3))
				{
					try
					{
						Traverse.Create((object)val).Method("RevertCombinedMeshes", Array.Empty<object>()).GetValue();
						num++;
						Log.LogInfo((object)$"[furn-revert] reverted MeshCombiner on '{((Object)((Component)val).gameObject).name}' (baked {array2.Length} objs, nearest {Mathf.Sqrt(num4):F1}u)");
					}
					catch (Exception ex)
					{
						Log.LogInfo((object)("[furn-revert] failed on '" + ((Object)((Component)val).gameObject).name + "': " + ex.Message));
					}
				}
			}
			return num;
		}

		private static bool HasSaveablePrefabInChain(Transform t)
		{
			Transform val = t;
			while ((Object)(object)val != (Object)null)
			{
				if ((Object)(object)((Component)val).GetComponent("SaveablePrefab") != (Object)null)
				{
					return true;
				}
				val = val.parent;
			}
			return false;
		}

		private int ClearFurnitureNear(Vector3 anchor, bool logKills, float radius = -1f, string anchorId = null)
		{
			//IL_0016: 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_0056: Unknown result type (might be due to invalid IL or missing references)
			//IL_0057: Unknown result type (might be due to invalid IL or missing references)
			//IL_005c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0144: Unknown result type (might be due to invalid IL or missing references)
			//IL_0149: Unknown result type (might be due to invalid IL or missing references)
			//IL_014a: Unknown result type (might be due to invalid IL or missing references)
			//IL_014f: Unknown result type (might be due to invalid IL or missing references)
			float num = ((radius > 0f) ? radius : CLEAR_RADIUS);
			float num2 = num * num;
			RevertCombinersNear(anchor, logKills, num);
			int num3 = 0;
			HashSet<GameObject> hashSet = new HashSet<GameObject>();
			MeshFilter[] array = Object.FindObjectsOfType<MeshFilter>();
			foreach (MeshFilter val in array)
			{
				if ((Object)(object)val == (Object)null)
				{
					continue;
				}
				Vector3 val2 = ((Component)val).transform.position - anchor;
				if (((Vector3)(ref val2)).sqrMagnitude > num2)
				{
					continue;
				}
				Transform val3 = ((Component)val).transform;
				GameObject val4 = null;
				while ((Object)(object)val3 != (Object)null)
				{
					if (IsFurnitureName(((Object)((Component)val3).gameObject).name))
					{
						val4 = ((Component)val3).gameObject;
						break;
					}
					val3 = val3.parent;
				}
				if ((Object)(object)val4 == (Object)null || !hashSet.Add(val4))
				{
					continue;
				}
				string name = ((Object)val4).name;
				float num4 = 0f;
				if (name.IndexOf("bush", StringComparison.OrdinalIgnoreCase) >= 0)
				{
					num4 = ((anchorId != null && _wideBushTaverns.Contains(anchorId)) ? (num * num) : 25f);
				}
				else if (name.IndexOf("light post a", StringComparison.OrdinalIgnoreCase) >= 0)
				{
					num4 = 36f;
				}
				else if (name.IndexOf("east_market_stall", StringComparison.OrdinalIgnoreCase) >= 0)
				{
					num4 = 64f;
				}
				if (num4 > 0f)
				{
					val2 = val4.transform.position - anchor;
					if (((Vector3)(ref val2)).sqrMagnitude > num4)
					{
						continue;
					}
				}
				if (HasSaveablePrefabInChain(val4.transform))
				{
					continue;
				}
				bool flag = false;
				Renderer[] componentsInChildren = val4.GetComponentsInChildren<Renderer>(true);
				foreach (Renderer val5 in componentsInChildren)
				{
					if ((Object)(object)val5 != (Object)null && val5.enabled && ((Component)val5).gameObject.activeInHierarchy)
					{
						flag = true;
						break;
					}
				}
				if (!flag)
				{
					continue;
				}
				int instanceID = ((Object)val4).GetInstanceID();
				if (_killedFurniture.Add(instanceID))
				{
					num3++;
					if (logKills)
					{
						Log.LogInfo((object)string.Format("[furn-kill] '{0}' static={1} parent='{2}'", ((Object)val4).name, val4.isStatic, Object.op_Implicit((Object)(object)val4.transform.parent) ? ((Object)val4.transform.parent).name : "none"));
					}
				}
				if ((Object)(object)val4.transform.parent != (Object)(object)Graveyard())
				{
					val4.transform.SetParent(Graveyard(), false);
				}
				if (val4.activeSelf)
				{
					val4.SetActive(false);
				}
			}
			return num3;
		}

		private Transform Graveyard()
		{
			//IL_0013: Unknown result type (might be due to invalid IL or missing references)
			//IL_0019: Expected O, but got Unknown
			//IL_002e: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)_graveyard == (Object)null)
			{
				GameObject val = new GameObject("PlayerHomeRemovedFurniture");
				val.transform.position = new Vector3(0f, -5000f, 0f);
				val.SetActive(false);
				_graveyard = val.transform;
			}
			return _graveyard;
		}

		private void SweepOwnedTavernFurniture()
		{
			//IL_0013: Unknown result type (might be due to invalid IL or missing references)
			//IL_0018: Unknown result type (might be due to invalid IL or missing references)
			//IL_005e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0063: Unknown result type (might be due to invalid IL or missing references)
			//IL_0064: Unknown result type (might be due to invalid IL or missing references)
			//IL_0065: Unknown result type (might be due to invalid IL or missing references)
			//IL_0083: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)Utilities.PlayerTransform == (Object)null)
			{
				return;
			}
			Vector3 position = Utilities.PlayerTransform.position;
			foreach (Home home in _homes)
			{
				if (!home.owned || !home.isClaimedTavern || (Object)(object)home.tavern == (Object)null)
				{
					continue;
				}
				Vector3 position2 = ((Component)home.tavern).transform.position;
				float num = Vector3.Distance(position, position2);
				float num2 = ClearRadiusFor(home);
				if (!(num > num2 + 25f))
				{
					int num3 = ClearFurnitureNear(position2, logKills: false, num2, home.def.id);
					if (num3 > 0)
					{
						Log.LogInfo((object)$"[furn-sweep] hid {num3} new furniture object(s); player {num:F0}u from anchor.");
					}
				}
			}
		}

		private void DisableTavernBusiness(Home home)
		{
			//IL_0076: Unknown result type (might be due to invalid IL or missing references)
			//IL_007b: Unknown result type (might be due to invalid IL or missing references)
			//IL_009c: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ac: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00cb: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d0: Unknown result type (might be due to invalid IL or missing references)
			//IL_0124: Unknown result type (might be due to invalid IL or missing references)
			//IL_0129: Unknown result type (might be due to invalid IL or missing references)
			//IL_012a: Unknown result type (might be due to invalid IL or missing references)
			//IL_012f: Unknown result type (might be due to invalid IL or missing references)
			//IL_017f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0184: Unknown result type (might be due to invalid IL or missing references)
			//IL_0185: Unknown result type (might be due to invalid IL or missing references)
			//IL_018a: 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_01df: Unknown result type (might be due to invalid IL or missing references)
			//IL_01e0: Unknown result type (might be due to invalid IL or missing references)
			//IL_01e5: Unknown result type (might be due to invalid IL or missing references)
			//IL_0235: Unknown result type (might be due to invalid IL or missing references)
			//IL_023a: Unknown result type (might be due to invalid IL or missing references)
			//IL_023b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0240: Unknown result type (might be due to invalid IL or missing references)
			//IL_02c4: Unknown result type (might be due to invalid IL or missing references)
			//IL_0290: Unknown result type (might be due to invalid IL or missing references)
			//IL_0295: Unknown result type (might be due to invalid IL or missing references)
			//IL_0296: Unknown result type (might be due to invalid IL or missing references)
			//IL_029b: Unknown result type (might be due to invalid IL or missing references)
			//IL_030a: Unknown result type (might be due to invalid IL or missing references)
			//IL_030f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0310: Unknown result type (might be due to invalid IL or missing references)
			//IL_0315: Unknown result type (might be due to invalid IL or missing references)
			GameObject root = home.root;
			if ((Object)(object)root == (Object)null)
			{
				return;
			}
			if ((Object)(object)home.tavern != (Object)null)
			{
				try
				{
					home.tavern.rawPrice = 0f;
					Traverse.Create((object)home.tavern).Method("UpdateTextAndPrice", Array.Empty<object>()).GetValue();
				}
				catch (Exception ex)
				{
					Log.LogWarning((object)("Could not zero tavern price: " + ex.Message));
				}
			}
			Vector3 position = root.transform.position;
			float num = float.MaxValue;
			GPButtonTavernSleep[] array = Object.FindObjectsOfType<GPButtonTavernSleep>();
			Vector3 val2;
			foreach (GPButtonTavernSleep val in array)
			{
				val2 = ((Component)val).transform.position - root.transform.position;
				float sqrMagnitude = ((Vector3)(ref val2)).sqrMagnitude;
				if (sqrMagnitude < num)
				{
					num = sqrMagnitude;
					position = ((Component)val).transform.position;
				}
			}
			float num2 = ClearRadiusFor(home);
			float num3 = Mathf.Min(14f, num2);
			float num4 = num2 * num2;
			float num5 = num3 * num3;
			int num6 = 0;
			int num7 = 0;
			Shopkeeper[] array2 = Object.FindObjectsOfType<Shopkeeper>();
			foreach (Shopkeeper val3 in array2)
			{
				if (Object.op_Implicit((Object)(object)val3))
				{
					val2 = ((Component)val3).transform.position - position;
					if (((Vector3)(ref val2)).sqrMagnitude <= num5)
					{
						Object.Destroy((Object)(object)((Component)val3).gameObject);
						num6++;
					}
				}
			}
			TavernRumorsDude[] array3 = Object.FindObjectsOfType<TavernRumorsDude>();
			foreach (TavernRumorsDude val4 in array3)
			{
				if (Object.op_Implicit((Object)(object)val4))
				{
					val2 = ((Component)val4).transform.position - position;
					if (((Vector3)(ref val2)).sqrMagnitude <= num4)
					{
						Object.Destroy((Object)(object)((Component)val4).gameObject);
						num6++;
					}
				}
			}
			NPCAnimations[] array4 = Object.FindObjectsOfType<NPCAnimations>();
			foreach (NPCAnimations val5 in array4)
			{
				if (Object.op_Implicit((Object)(object)val5))
				{
					val2 = ((Component)val5).transform.position - position;
					if (((Vector3)(ref val2)).sqrMagnitude <= num5)
					{
						Object.Destroy((Object)(object)((Component)val5).gameObject);
						num6++;
					}
				}
			}
			ShopItemSpawner[] array5 = Object.FindObjectsOfType<ShopItemSpawner>();
			foreach (ShopItemSpawner val6 in array5)
			{
				if (Object.op_Implicit((Object)(object)val6))
				{
					val2 = ((Component)val6).transform.position - position;
					if (((Vector3)(ref val2)).sqrMagnitude <= num4)
					{
						Object.Destroy((Object)(object)((Component)val6).gameObject);
						num7++;
					}
				}
			}
			ShopArea[] array6 = Object.FindObjectsOfType<ShopArea>();
			foreach (ShopArea val7 in array6)
			{
				if (Object.op_Implicit((Object)(object)val7))
				{
					val2 = ((Component)val7).transform.position - position;
					if (((Vector3)(ref val2)).sqrMagnitude <= num4)
					{
						Object.Destroy((Object)(object)val7);
						num7++;
					}
				}
			}
			int num8 = ClearFurnitureNear(position, logKills: true, num2, home.def.id);
			HashSet<string> hashSet = new HashSet<string>();
			MeshFilter[] array7 = Object.FindObjectsOfType<MeshFilter>();
			foreach (MeshFilter val8 in array7)
			{
				if ((Object)(object)val8 == (Object)null)
				{
					continue;
				}
				val2 = ((Component)val8).transform.position - position;
				if (!(((Vector3)(ref val2)).sqrMagnitude > num4))
				{
					string name = ((Object)((Component)val8).gameObject).name;
					string text = (((Object)(object)((Component)val8).transform.parent != (Object)null) ? ((Object)((Component)((Component)val8).transform.parent).gameObject).name : "(no parent)");
					string item = name + " <- " + text;
					if (hashSet.Add(item))
					{
						Log.LogInfo((object)("[near-mesh] '" + name + "'  parent='" + text + "'"));
					}
				}
			}
			Log.LogInfo((object)$"[claim] '{home.def.id}': removed {num6} people + {num7} shop + {num8} furniture within {num2:F0}u of anchor, kept free inn rest.");
		}

		private static string CleanName(string raw)
		{
			string text = raw.Replace("(Clone)", "").Trim();
			int num = text.IndexOf(' ');
			if (num > 0 && int.TryParse(text.Substring(0, num), out var _))
			{
				text = text.Substring(num + 1);
			}
			return text;
		}

		private List<HomeDef> LoadHomesConfig()
		{
			List<HomeDef> list = new List<HomeDef>();
			try
			{
				if (!File.Exists(HomesTxtPath))
				{
					WriteExampleHomes();
					return list;
				}
				string[] array = File.ReadAllLines(HomesTxtPath);
				for (int i = 0; i < array.Length; i++)
				{
					string text = array[i].Trim();
					if (text.Length == 0 || text.StartsWith("#"))
					{
						continue;
					}
					string[] array2 = text.Split(new char[1] { '|' });
					if (array2.Length < 10)
					{
						Log.LogWarning((object)("homes.txt: need 10 fields -> " + text));
						continue;
					}
					try
					{
						list.Add(new HomeDef
						{
							id = array2[0].Trim(),
							prefab = array2[1].Trim(),
							bedPrefab = array2[2].Trim(),
							x = F(array2[3]),
							y = F(array2[4]),
							z = F(array2[5]),
							yaw = F(array2[6]),
							price = (int)F(array2[7]),
							currency = (int)F(array2[8]),
							storeRadius = F(array2[9])
						});
					}
					catch (Exception ex)
					{
						Log.LogWarning((object)("homes.txt skipped line: " + text + " (" + ex.Message + ")"));
					}
				}
			}
			catch (Exception ex2)
			{
				Log.LogError((object)("Could not read homes.txt: " + ex2.Message));
			}
			return list;
		}

		private static float F(string s)
		{
			return float.Parse(s.Trim(), CultureInfo.InvariantCulture);
		}

		private void WriteExampleHomes()
		{
			string[] contents = new string[11]
			{
				"# Player Home definitions. One home per line. Lines starting with # are ignored.", "#   id | prefab | bedPrefab | x | y | z | yaw | price | currency | storeRadius", "# - id: any unique label (remembers ownership + stored cargo). Don't reuse.", "# - prefab/bedPrefab: names from the prefab dump (bedPrefab may be left empty).", "# - x y z: a STABLE position. Press F8 in-game where you want a home to log a", "#   ready-to-paste line, then copy it below.", "# - price: in GOLD (homes cost universal gold, like boats). currency is ignored.", "# - use a period for decimals (e.g. 12.5), regardless of your Windows language.", "#", "# example (replace prefab + numbers with a real one captured via F8):",
				"# myhome | TAVERN |  | 0 | 0 | 0 | 0 | 25000 | -1 | 12"
			};
			File.WriteAllLines(HomesTxtPath, contents);
			Log.LogInfo((object)"Wrote example homes.txt — capture a spot with F8, then add a line.");
		}

		private void DumpPrefabs()
		{
			//IL_0020: Unknown result type (might be due to invalid IL or missing references)
			//IL_0025: Unknown result type (might be due to invalid IL or missing references)
			Log.LogInfo((object)"==== PLAYER HOME: prefab dump (filtered) ====");
			foreach (SailwindPrefab prefab in GameAssets.Prefabs)
			{
				SailwindPrefab current = prefab;
				string text = ((SailwindPrefab)(ref current)).PrefabName.ToLower();
				if (text.Contains("tavern") || text.Contains("gallery") || text.Contains("house") || text.Contains("hut") || text.Contains("building") || text.Contains("shack") || text.Contains("studio") || text.Contains("art") || text.Contains("bed") || text.Contains("room") || text.Contains("home") || text.Contains("shop"))
				{
					Log.LogInfo((object)("  " + ((SailwindPrefab)(ref current)).FullPrefabName));
				}
			}
			Log.LogInfo((object)"==== end dump (set DumpPrefabs=false when done) ====");
		}

		private void ProbeNearby()
		{
			//IL_001f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0013: Unknown result type (might be due to invalid IL or missing references)
			//IL_0024: Unknown result type (might be due to invalid IL or missing references)
			//IL_0038: Unknown result type (might be due to invalid IL or missing references)
			//IL_0034: Unknown result type (might be due to invalid IL or missing references)
			//IL_003d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0063: Unknown result type (might be due to invalid IL or missing references)
			//IL_0068: Unknown result type (might be due to invalid IL or missing references)
			//IL_0069: Unknown result type (might be due to invalid IL or missing references)
			//IL_006e: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a9: 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_00ae: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00dd: Unknown result type (might be due to invalid IL or missing references)
			//IL_00df: Unknown result type (might be due to invalid IL or missing references)
			//IL_0198: Unknown result type (might be due to invalid IL or missing references)
			//IL_019d: Unknown result type (might be due to invalid IL or missing references)
			//IL_02a7: Unknown result type (might be due to invalid IL or missing references)
			//IL_02ac: Unknown result type (might be due to invalid IL or missing references)
			//IL_02dc: Unknown result type (might be due to invalid IL or missing references)
			//IL_02e1: Unknown result type (might be due to invalid IL or missing references)
			Vector3 val = (((Object)(object)Utilities.PlayerTransform != (Object)null) ? Utilities.PlayerTransform.position : Vector3.zero);
			Home home = NearestOwnedHome();
			bool flag = home != null;
			Vector3 val2 = (flag ? HomeAnchor(home) : val);
			Tavern val3 = null;
			float num = float.MaxValue;
			Tavern[] array = Object.FindObjectsOfType<Tavern>();
			foreach (Tavern val4 in array)
			{
				Vector3 val5 = ((Component)val4).transform.position - val;
				float sqrMagnitude = ((Vector3)(ref val5)).sqrMagnitude;
				if (sqrMagnitude < num)
				{
					num = sqrMagnitude;
					val3 = val4;
				}
			}
			Vector3 val6 = (((Object)(object)val3 != (Object)null) ? ((Component)val3).transform.position : val);
			string tid = (((Object)(object)val3 != (Object)null) ? $"tavern@{Mathf.Round(WorldToReal(val6).x)}_{Mathf.Round(WorldToReal(val6).z)}" : "none");
			string[] array2 = new string[11]
			{
				"CenterEyeAnchor", "OVRCameraRig", "OVRPlayerController", "TrackingSpace", "Skybox", "Z environment", "RealisticSkies", "needs UI", "needs minibars", "reputation notif",
				"head (cam)"
			};
			List<KeyValuePair<float, string>> list = new List<KeyValuePair<float, string>>();
			HashSet<int> hashSet = new HashSet<int>();
			MeshFilter[] array3 = Object.FindObjectsOfType<MeshFilter>();
			foreach (MeshFilter val7 in array3)
			{
				if ((Object)(object)val7 == (Object)null)
				{
					continue;
				}
				float num2 = Vector3.Distance(((Component)val7).transform.position, val);
				if (num2 > 20f)
				{
					continue;
				}
				GameObject gameObject = ((Component)val7).gameObject;
				if (!hashSet.Add(((Object)gameObject).GetInstanceID()))
				{
					continue;
				}
				string text = ((Object)gameObject).name;
				Transform parent = gameObject.transform.parent;
				for (int j = 0; j < 8; j++)
				{
					if (!((Object)(object)parent != (Object)null))
					{
						break;
					}
					text = ((Object)parent).name + " / " + text;
					parent = parent.parent;
				}
				bool flag2 = false;
				string[] array4 = array2;
				foreach (string value in array4)
				{
					if (text.IndexOf(value, StringComparison.OrdinalIgnoreCase) >= 0)
					{
						flag2 = true;
						break;
					}
				}
				if (!flag2)
				{
					Renderer component = gameObject.GetComponent<Renderer>();
					string text2 = (((Object)(object)component == (Object)null) ? "no-renderer" : ("renderer=" + (component.enabled ? "ON" : "OFF(baked?)")));
					string text3 = (flag ? $"anchor={Vector3.Distance(((Component)val7).transform.position, val2):F1}m " : "");
					string text4 = (((Object)(object)val3 != (Object)null) ? $"tav={Vector3.Distance(((Component)val7).transform.position, val6):F1}m " : "");
					list.Add(new KeyValuePair<float, string>(num2, $"[probe] d={num2:F1}m {text4}{text3}static={gameObject.isStatic} {text2} | {text}"));
				}
			}
			list.Sort((KeyValuePair<float, string> a, KeyValuePair<float, string> b) => a.Key.CompareTo(b.Key));
			string text5 = (flag ? $" | nearest owned anchor '{home.def.id}' (clear radius {ClearRadiusFor(home):F0}m)" : "");
			object obj;
			if (!((Object)(object)val3 != (Object)null))
			{
				obj = "";
			}
			else
			{
				string arg = tid;
				Home home2 = _homes.Find((Home h) => h.def != null && h.def.id == tid);
				obj = $" | nearest tavern '{arg}' (clear radius {((home2 != null) ? ClearRadiusFor(home2) : CLEAR_RADIUS):F0}m)";
			}
			string text6 = (string)obj;
			Log.LogInfo((object)$"==== PROBE: {list.Count} world mesh(es) within {20f}m of player (UI/camera/sky filtered out){text5}{text6} ====");
			for (int num3 = 0; num3 < list.Count && num3 < 60; num3++)
			{
				Log.LogInfo((object)list[num3].Value);
			}
			Log.LogInfo((object)"==== end probe ====");
		}

		internal void Notify(string msg)
		{
			try
			{
				NotificationUi instance = NotificationUi.instance;
				if (instance != null)
				{
					instance.ShowNotification(msg);
				}
			}
			catch
			{
				Log.LogInfo((object)("[notify] " + msg));
			}
		}
	}
	[HarmonyPatch(typeof(Tavern), "ClickSleepButton")]
	internal static class Tavern_ClickSleep_Patch
	{
		private static void Postfix(Tavern __instance)
		{
			PlayerHomePlugin i = PlayerHomePlugin.I;
			if (!((Object)(object)i == (Object)null))
			{
				bool flag = i.OwnsTavern(__instance);
				i.StartSleepTrace(flag ? "owned tavern ClickSleepButton" : "UNOWNED tavern ClickSleepButton");
			}
		}
	}
	[HarmonyPatch(typeof(Tavern), "UpdateTextAndPrice")]
	internal static class Tavern_UpdateText_Patch
	{
		private static void Postfix(Tavern __instance)
		{
			PlayerHomePlugin i = PlayerHomePlugin.I;
			if ((Object)(object)i == (Object)null || !i.OwnsTavern(__instance))
			{
				return;
			}
			try
			{
				Component value = Traverse.Create((object)__instance).Field("text").GetValue<Component>();
				if ((Object)(object)value == (Object)null)
				{
					return;
				}
				Traverse.Create((object)value).Property("text", (object[])null).SetValue((object)"Welcome Home");
				Transform val = value.transform;
				for (int j = 0; j < 3; j++)
				{
					if (!((Object)(object)val.parent != (Object)null))
					{
						break;
					}
					val = val.parent;
				}
				Component[] componentsInChildren = ((Component)val).GetComponentsInChildren<Component>(true);
				foreach (Component val2 in componentsInChildren)
				{
					if (!((Object)(object)val2 == (Object)null) && !((Object)(object)val2 == (Object)(object)value) && !(((object)val2).GetType().Name != "TextMesh"))
					{
						Traverse val3 = Traverse.Create((object)val2).Property("text", (object[])null);
						string text = val3.GetValue<string>() ?? "";
						if (text.Contains("Rent a room") || text.Contains("Food and drink") || text.Contains("until next morning") || text.Contains("included in the price") || text.Contains("next morning"))
						{
							val3.SetValue((object)"");
						}
					}
				}
			}
			catch
			{
			}
		}
	}
	public class Home
	{
		public HomeDef def;

		public GameObject root;

		public Vector3 realPos;

		public bool owned;

		public bool isClaimedTavern;

		public Tavern tavern;

		public HomeRestButton restButton;

		public HomeBuyButton buyButton;

		public bool buttonAnchored;

		public bool buyLabelAdded;

		public Component buyLabelTM;

		public bool priceConfirmed;

		public bool playerInside;
	}
	public class HomeDef
	{
		public string id = "";

		public string prefab = "";

		public string bedPrefab = "";

		public float x;

		public float y;

		public float z;

		public float yaw;

		public int price = 25000;

		public int currency = -1;

		public float storeRadius = 12f;
	}
	[Serializable]
	public class SaveData
	{
		public List<string> ownedIds = new List<string>();

		public List<Warehouse> warehouses = new List<Warehouse>();
	}
	[Serializable]
	public class Warehouse
	{
		public string homeId = "";

		public List<StoredItem> items = new List<StoredItem>();
	}
	[Serializable]
	public class StoredItem
	{
		public string prefab = "";

		public string blob = "";

		public float px;

		public float py;

		public float pz;

		public float qx;

		public float qy;

		public float qz;

		public float qw;
	}
	public class HomeRestButton : GoPointerButton
	{
		public Home home;

		public override void OnActivate()
		{
			PlayerHomePlugin.I?.Rest(home);
		}
	}
	public class HomeBuyButton : GoPointerButton
	{
		public Home home;

		public override void OnActivate()
		{
			PlayerHomePlugin.I?.TryBuy(home);
		}
	}
}