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 _dumpPrefabs; private ConfigEntry _enableTavernClaim; private ConfigEntry _traceSleep; private ConfigEntry _captureKey; private ConfigEntry _probeKey; private ConfigEntry _persistItems; private ConfigEntry _recordKey; private ConfigEntry _restoreKey; private ConfigEntry _clearRadius; private ConfigEntry _furnishRadius; private ConfigEntry _clearOverrides; private readonly Dictionary _clearRadiusOverrides = new Dictionary(); private ConfigEntry _excludedTaverns; private readonly HashSet _excludedIds = new HashSet(); private ConfigEntry _priceOverrides; private readonly Dictionary _priceOverrideMap = new Dictionary(); private readonly List _homes = new List(); 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 PriceById = new Dictionary { { "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> PriceTable = new List> { new KeyValuePair("nilem", 30), new KeyValuePair("neverdin", 30), new KeyValuePair("oasis", 30), new KeyValuePair("fort aestrin", 100), new KeyValuePair("malific", 150), new KeyValuePair("eastwind", 40), new KeyValuePair("siren", 75), new KeyValuePair("happy", 150), new KeyValuePair("dragon", 50), new KeyValuePair("crab", 80), new KeyValuePair("sage", 80), new KeyValuePair("firefly", 150), new KeyValuePair("dead cove", 75), new KeyValuePair("chronos", 75), new KeyValuePair("aestrin", 100) }; private const int DEFAULT_PRICE = 75; private static readonly List> PriceByTown = new List> { new KeyValuePair("nilem", 30), new KeyValuePair("neverdin", 30), new KeyValuePair("oasis", 30), new KeyValuePair("old ankh", 120), new KeyValuePair("ankh", 120), new KeyValuePair("fort aestrin", 100), new KeyValuePair("malefic", 150), new KeyValuePair("eastwind", 40), new KeyValuePair("siren", 75), new KeyValuePair("happy", 150), new KeyValuePair("dragon", 50), new KeyValuePair("crab", 80), new KeyValuePair("sage", 80), new KeyValuePair("firefly", 150), new KeyValuePair("turtle", 75), new KeyValuePair("dead cove", 75), new KeyValuePair("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 _clearRadiusDefaults = new Dictionary { { "tavern@-48907_-46062", 10f } }; private readonly HashSet _killedFurniture = new HashSet(); private static readonly HashSet _wideBushTaverns = new HashSet { "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("Setup", "DumpPrefabs", false, "Log candidate building/bed prefab names after a save loads. Turn off when done."); _enableTavernClaim = ((BaseUnityPlugin)this).Config.Bind("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("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("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("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("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("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("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("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("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("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("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("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().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(((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 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(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(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(); int num2 = Resources.FindObjectsOfTypeAll().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(); 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(); _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(); 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.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.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(); 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(); Material val = (((Object)(object)component != (Object)null) ? ((Renderer)component).sharedMaterial : null); float num = Traverse.Create((object)value).Property("characterSize", (object[])null).GetValue(); int num2 = Traverse.Create((object)value).Property("fontSize", (object[])null).GetValue(); 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(); 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 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(); 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 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 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 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 LiveInstanceIds() { HashSet hashSet = new HashSet(); SaveablePrefab[] array = Object.FindObjectsOfType(); 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 list = new List(); SaveablePrefab[] array = Object.FindObjectsOfType(); foreach (SaveablePrefab val2 in array) { if ((Object)(object)val2 == (Object)null) { continue; } try { if (Traverse.Create((object)val2).Method("GetParentObject", Array.Empty()).GetValue() > 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()).GetValue(); 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 hashSet = LiveInstanceIds(); PrefabsDirectory val = Object.FindObjectOfType(); GameObject[] array = (((Object)(object)val != (Object)null) ? Traverse.Create((object)val).Field("directory").GetValue() : 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(array[prefabIndex]); SaveablePrefab component = val3.GetComponent(); 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(true); for (int i = 0; i < componentsInChildren.Length; i++) { Object.Destroy((Object)(object)componentsInChildren[i]); } GPButtonTavernSleep[] componentsInChildren2 = building.GetComponentsInChildren(true); for (int i = 0; i < componentsInChildren2.Length; i++) { Object.Destroy((Object)(object)((Component)componentsInChildren2[i]).gameObject); } Shopkeeper[] componentsInChildren3 = building.GetComponentsInChildren(true); for (int i = 0; i < componentsInChildren3.Length; i++) { Object.Destroy((Object)(object)((Component)componentsInChildren3[i]).gameObject); } ShopItemSpawner[] componentsInChildren4 = building.GetComponentsInChildren(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(); 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(); } 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()).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 hashSet = new HashSet(); MeshFilter[] array = Object.FindObjectsOfType(); 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(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()).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(); 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(); 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(); 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(); 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(); 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(); 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 hashSet = new HashSet(); MeshFilter[] array7 = Object.FindObjectsOfType(); 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 LoadHomesConfig() { List list = new List(); 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(); 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> list = new List>(); HashSet hashSet = new HashSet(); MeshFilter[] array3 = Object.FindObjectsOfType(); 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(); 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(num2, $"[probe] d={num2:F1}m {text4}{text3}static={gameObject.isStatic} {text2} | {text}")); } } list.Sort((KeyValuePair a, KeyValuePair 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(); 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(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() ?? ""; 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 ownedIds = new List(); public List warehouses = new List(); } [Serializable] public class Warehouse { public string homeId = ""; public List items = new List(); } [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); } } }