Decompiled source of AncestralRest v1.0.0

AncestralRest.dll

Decompiled 15 hours ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Text;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using UnityEngine;
using UnityEngine.SceneManagement;

[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: AssemblyVersion("0.0.0.0")]
namespace AncestralRest;

[BepInPlugin("com.spencer4792.ancestralrest", "AncestralRest", "1.0.0")]
public class AncestralRestPlugin : BaseUnityPlugin
{
	private class RestEntry
	{
		public double EnterTime;

		public string ChestUID;

		public float MissingSince;
	}

	public const string GUID = "com.spencer4792.ancestralrest";

	public const string NAME = "AncestralRest";

	public const string VERSION = "1.0.0";

	internal static ManualLogSource Log;

	private static ConfigEntry<float> s_hoursRequired;

	private static ConfigEntry<float> s_checkInterval;

	private static readonly Dictionary<string, RestEntry> s_resting = new Dictionary<string, RestEntry>();

	private float m_nextCheck;

	private static readonly FieldInfo s_itemPrefabsField = typeof(ResourcesPrefabManager).GetField("ITEM_PREFABS", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);

	private static readonly List<ItemContainer> s_chestCache = new List<ItemContainer>();

	private static string s_cacheScene = "";

	private static float s_cacheExpiry;

	private static string LedgerPath => Path.Combine(Paths.ConfigPath, "AncestralRest-ledger.txt");

	private void Awake()
	{
		Log = ((BaseUnityPlugin)this).Logger;
		s_hoursRequired = ((BaseUnityPlugin)this).Config.Bind<float>("General", "HoursRequired", 24f, "In-game hours an item must rest inside a legacy chest before it transmutes.");
		s_checkInterval = ((BaseUnityPlugin)this).Config.Bind<float>("General", "CheckIntervalSeconds", 2f, "How often (real seconds) chest contents are checked.");
		LoadLedger();
		Log.LogMessage((object)("AncestralRest 1.0.0 ready. Rest duration: " + s_hoursRequired.Value + " game-hours."));
	}

	private void Update()
	{
		if (KeyDown((KeyCode)292))
		{
			if (InputHelper.GetKey((KeyCode)306) || InputHelper.GetKey((KeyCode)305))
			{
				EvacuateChests();
			}
			else
			{
				DumpLegacyState();
			}
		}
		else if (!(Time.unscaledTime < m_nextCheck))
		{
			m_nextCheck = Time.unscaledTime + Mathf.Max(0.5f, s_checkInterval.Value);
			try
			{
				Sweep();
			}
			catch (Exception ex)
			{
				Log.LogError((object)("Sweep failed: " + ex));
			}
		}
	}

	private void EvacuateChests()
	{
		try
		{
			Character val = ((!((Object)(object)CharacterManager.Instance != (Object)null)) ? null : CharacterManager.Instance.GetFirstLocalCharacter());
			if ((Object)(object)val == (Object)null)
			{
				return;
			}
			int num = 0;
			foreach (ItemContainer item in EnumerateLegacyChests())
			{
				List<Item> list = new List<Item>();
				foreach (Item containedItem in item.GetContainedItems())
				{
					if ((Object)(object)containedItem != (Object)null)
					{
						list.Add(containedItem);
					}
				}
				foreach (Item item2 in list)
				{
					s_resting.Remove(item2.UID);
					val.Inventory.TakeItem(item2.UID);
					Log.LogMessage((object)("Evacuated '" + item2.Name + "' from '" + ((Item)item).Name + "' to your bag."));
					num++;
				}
			}
			SaveLedger();
			Log.LogMessage((object)("Evacuation done: " + num + " item(s)."));
		}
		catch (Exception ex)
		{
			Log.LogError((object)("EvacuateChests: " + ex));
		}
	}

	private static bool KeyDown(KeyCode k)
	{
		//IL_0000: Unknown result type (might be due to invalid IL or missing references)
		return InputHelper.GetKeyDown(k);
	}

	private static bool IsEffectivelyInChest(Item it, ItemContainer chest)
	{
		//IL_0044: Unknown result type (might be due to invalid IL or missing references)
		//IL_004f: Unknown result type (might be due to invalid IL or missing references)
		if ((Object)(object)it == (Object)null || (Object)(object)chest == (Object)null)
		{
			return false;
		}
		if (object.ReferenceEquals(it.ParentContainer, chest))
		{
			return true;
		}
		if ((Object)(object)((EffectSynchronizer)it).OwnerCharacter == (Object)null && Vector3.Distance(((Component)it).transform.position, ((Component)chest).transform.position) < 3f)
		{
			return true;
		}
		return false;
	}

	private void Sweep()
	{
		if ((Object)(object)ItemManager.Instance == (Object)null)
		{
			return;
		}
		Character val = ((!((Object)(object)CharacterManager.Instance != (Object)null)) ? null : CharacterManager.Instance.GetFirstLocalCharacter());
		if ((Object)(object)val == (Object)null)
		{
			return;
		}
		double gameTime = EnvironmentConditions.GameTime;
		bool flag = false;
		Dictionary<string, ItemContainer> dictionary = new Dictionary<string, ItemContainer>();
		foreach (ItemContainer item2 in EnumerateLegacyChests())
		{
			if (!dictionary.ContainsKey(((Item)item2).UID))
			{
				dictionary.Add(((Item)item2).UID, item2);
			}
		}
		foreach (KeyValuePair<string, ItemContainer> item3 in dictionary)
		{
			ItemContainer value = item3.Value;
			List<Item> list = new List<Item>();
			foreach (Item containedItem in value.GetContainedItems())
			{
				if ((Object)(object)containedItem != (Object)null)
				{
					list.Add(containedItem);
				}
			}
			foreach (Item item4 in list)
			{
				if (item4.LegacyItemID > 0 && !s_resting.ContainsKey(item4.UID))
				{
					s_resting[item4.UID] = new RestEntry
					{
						EnterTime = gameTime,
						ChestUID = ((Item)value).UID
					};
					flag = true;
					Log.LogMessage((object)("'" + item4.Name + "' begins its ancestral rest (" + s_hoursRequired.Value + " game-hours until transmutation)."));
				}
			}
		}
		List<string> list2 = new List<string>();
		foreach (KeyValuePair<string, RestEntry> item5 in s_resting)
		{
			dictionary.TryGetValue(item5.Value.ChestUID, out var value2);
			if ((Object)(object)value2 == (Object)null)
			{
				continue;
			}
			Item item = ItemManager.Instance.GetItem(item5.Key);
			if (!((Object)(object)item != (Object)null) || !IsEffectivelyInChest(item, value2))
			{
				if (item5.Value.MissingSince == 0f)
				{
					item5.Value.MissingSince = Time.unscaledTime;
				}
				else if (Time.unscaledTime - item5.Value.MissingSince > 60f)
				{
					list2.Add(item5.Key);
				}
			}
			else
			{
				item5.Value.MissingSince = 0f;
				if (gameTime - item5.Value.EnterTime >= (double)s_hoursRequired.Value && Transmute(value2, item))
				{
					list2.Add(item5.Key);
					flag = true;
				}
			}
		}
		foreach (string item6 in list2)
		{
			s_resting.Remove(item6);
			flag = true;
		}
		if (flag)
		{
			SaveLedger();
		}
	}

	private IEnumerable<ItemContainer> EnumerateLegacyChests()
	{
		int fromManager = 0;
		IEnumerable raw = ((!((Object)(object)ItemManager.Instance != (Object)null)) ? null : ItemManager.Instance.WorldLegacyChests);
		if (raw != null)
		{
			IEnumerator enumerator = raw.GetEnumerator();
			try
			{
				while (enumerator.MoveNext())
				{
					object o = enumerator.Current;
					ItemContainer c = (ItemContainer)((o is ItemContainer) ? o : null);
					if ((Object)(object)c == (Object)null)
					{
						Type type = o?.GetType();
						if (type != null && type.IsGenericType)
						{
							PropertyInfo property = type.GetProperty("Value");
							if (property != null)
							{
								object? value = property.GetValue(o, null);
								c = (ItemContainer)((value is ItemContainer) ? value : null);
							}
						}
					}
					if ((Object)(object)c != (Object)null && ((Component)c).gameObject.activeInHierarchy)
					{
						fromManager++;
						yield return c;
					}
				}
			}
			finally
			{
				IDisposable disposable2;
				IDisposable disposable = (disposable2 = enumerator as IDisposable);
				if (disposable2 != null)
				{
					disposable.Dispose();
				}
			}
		}
		if (fromManager > 0)
		{
			yield break;
		}
		Scene activeScene = SceneManager.GetActiveScene();
		string scene = ((Scene)(ref activeScene)).name;
		if (scene != s_cacheScene || Time.unscaledTime > s_cacheExpiry || s_chestCache.Contains(null))
		{
			s_chestCache.Clear();
			if (Object.FindObjectsOfType(typeof(ItemContainer)) is ItemContainer[] array)
			{
				ItemContainer[] array2 = array;
				foreach (ItemContainer val in array2)
				{
					if ((Object)(object)val != (Object)null && ((Item)val).IsLegacyChest)
					{
						s_chestCache.Add(val);
					}
				}
			}
			s_cacheScene = scene;
			s_cacheExpiry = Time.unscaledTime + 20f;
		}
		foreach (ItemContainer c2 in s_chestCache)
		{
			if ((Object)(object)c2 != (Object)null && ((Component)c2).gameObject.activeInHierarchy)
			{
				yield return c2;
			}
		}
	}

	private bool Transmute(ItemContainer container, Item oldItem)
	{
		int legacyItemID = oldItem.LegacyItemID;
		Item val = ItemManager.Instance.GenerateItemNetwork(legacyItemID);
		if ((Object)(object)val == (Object)null)
		{
			Log.LogWarning((object)("Could not generate legacy item " + legacyItemID + " for '" + oldItem.Name + "' — leaving it untouched."));
			return false;
		}
		Equipment val2 = (Equipment)(object)((oldItem is Equipment) ? oldItem : null);
		Equipment val3 = (Equipment)(object)((val is Equipment) ? val : null);
		if ((Object)(object)val2 != (Object)null && (Object)(object)val3 != (Object)null && ((Item)val2).IsEnchanted)
		{
			try
			{
				foreach (int activeEnchantmentID in val2.ActiveEnchantmentIDs)
				{
					val3.AddEnchantment(activeEnchantmentID, false);
				}
			}
			catch (Exception ex)
			{
				Log.LogWarning((object)("Enchant transfer failed for '" + oldItem.Name + "': " + ex.Message));
			}
		}
		string name = oldItem.Name;
		ItemManager.Instance.DestroyItem(oldItem.UID);
		container.TryMoveItemToContainer(val);
		Log.LogMessage((object)("'" + name + "' has transcended: it is now '" + val.Name + "'."));
		return true;
	}

	private void DumpLegacyState()
	{
		try
		{
			double num = ((!((Object)(object)EnvironmentConditions.Instance != (Object)null)) ? (-1.0) : EnvironmentConditions.GameTime);
			Log.LogMessage((object)("=== AncestralRest dump (v1.0.0) GameTime=" + num.ToString("F2") + " ==="));
			IDictionary dictionary = ((!(s_itemPrefabsField != null)) ? null : (s_itemPrefabsField.GetValue(null) as IDictionary));
			if (dictionary != null)
			{
				int num2 = 0;
				foreach (DictionaryEntry item in dictionary)
				{
					object? value = item.Value;
					Item val = (Item)((value is Item) ? value : null);
					if (!((Object)(object)val == (Object)null) && val.LegacyItemID > 0)
					{
						num2++;
						Item val2 = ((ResourcesPrefabManager.Instance == null) ? null : ResourcesPrefabManager.Instance.GetItemPrefab(val.LegacyItemID));
						Log.LogMessage((object)("PAIR " + val.ItemID + " '" + val.Name + "' -> " + val.LegacyItemID + " '" + ((!((Object)(object)val2 != (Object)null)) ? "?" : val2.Name) + "'"));
					}
				}
				Log.LogMessage((object)(num2 + " legacy pair(s) in loaded prefabs."));
			}
			else
			{
				Log.LogMessage((object)"ITEM_PREFABS not accessible.");
			}
			IEnumerable enumerable = ((!((Object)(object)ItemManager.Instance != (Object)null)) ? null : ItemManager.Instance.WorldLegacyChests);
			int num3 = 0;
			string text = "";
			if (enumerable != null)
			{
				foreach (object item2 in enumerable)
				{
					num3++;
					if (num3 <= 3 && item2 != null)
					{
						text = text + item2.GetType().Name + " ";
					}
				}
			}
			Log.LogMessage((object)("WorldLegacyChests raw: " + ((enumerable != null) ? enumerable.GetType().Name : "null") + " count=" + num3 + ((text.Length <= 0) ? "" : (" elemTypes=" + text))));
			int num4 = 0;
			foreach (ItemContainer item3 in EnumerateLegacyChests())
			{
				num4++;
				Log.LogMessage((object)("CHEST '" + ((Item)item3).Name + "' UID=" + ((Item)item3).UID + " items=" + item3.ItemCount + " IsLegacyChest=" + ((Item)item3).IsLegacyChest));
				foreach (Item containedItem in item3.GetContainedItems())
				{
					if ((Object)(object)containedItem != (Object)null)
					{
						Log.LogMessage((object)("    contains '" + containedItem.Name + "' LegacyItemID=" + containedItem.LegacyItemID + " UID=" + containedItem.UID));
					}
				}
			}
			Log.LogMessage((object)(num4 + " legacy chest(s) usable in loaded scene."));
			foreach (KeyValuePair<string, RestEntry> item4 in s_resting)
			{
				double num5 = num - item4.Value.EnterTime;
				string text2 = "unresolved";
				Item val3 = ((!((Object)(object)ItemManager.Instance != (Object)null)) ? null : ItemManager.Instance.GetItem(item4.Key));
				text2 = ((!((Object)(object)val3 == (Object)null)) ? ("'" + val3.Name + "' owner=" + ((!((Object)(object)((EffectSynchronizer)val3).OwnerCharacter != (Object)null)) ? "none" : ((EffectSynchronizer)val3).OwnerCharacter.Name) + " parentContainer=" + ((!((Object)(object)val3.ParentContainer != (Object)null)) ? "none" : ((Item)val3.ParentContainer).UID.ToString())) : "ITEM NOT FOUND by UID");
				Log.LogMessage((object)("RESTING item=" + item4.Key + " chest=" + item4.Value.ChestUID + " elapsed=" + num5.ToString("F2") + "h / " + s_hoursRequired.Value + "h  [" + text2 + "]"));
			}
			Log.LogMessage((object)"=== end dump ===");
		}
		catch (Exception ex)
		{
			Log.LogError((object)("DumpLegacyState: " + ex));
		}
	}

	private static void LoadLedger()
	{
		try
		{
			if (!File.Exists(LedgerPath))
			{
				return;
			}
			string[] array = File.ReadAllLines(LedgerPath);
			foreach (string text in array)
			{
				string[] array2 = text.Split(new char[1] { '\t' });
				if (array2.Length >= 3 && double.TryParse(array2[1], out var result))
				{
					s_resting[array2[0]] = new RestEntry
					{
						EnterTime = result,
						ChestUID = array2[2]
					};
				}
			}
			Log.LogInfo((object)("Ledger loaded: " + s_resting.Count + " resting item(s)."));
		}
		catch (Exception ex)
		{
			Log.LogWarning((object)("Ledger load failed: " + ex.Message));
		}
	}

	private static void SaveLedger()
	{
		try
		{
			StringBuilder stringBuilder = new StringBuilder();
			foreach (KeyValuePair<string, RestEntry> item in s_resting)
			{
				stringBuilder.Append(item.Key).Append('\t').Append(item.Value.EnterTime)
					.Append('\t')
					.Append(item.Value.ChestUID)
					.Append('\n');
			}
			File.WriteAllText(LedgerPath, stringBuilder.ToString());
		}
		catch (Exception ex)
		{
			Log.LogWarning((object)("Ledger save failed: " + ex.Message));
		}
	}
}
internal static class InputHelper
{
	private static readonly Type s_inputType = Type.GetType("UnityEngine.Input, UnityEngine.InputLegacyModule");

	private static readonly MethodInfo s_getKeyDown = ((!(s_inputType != null)) ? null : s_inputType.GetMethod("GetKeyDown", new Type[1] { typeof(KeyCode) }));

	private static readonly MethodInfo s_getKey = ((!(s_inputType != null)) ? null : s_inputType.GetMethod("GetKey", new Type[1] { typeof(KeyCode) }));

	public static bool GetKeyDown(KeyCode k)
	{
		//IL_0020: Unknown result type (might be due to invalid IL or missing references)
		if (s_getKeyDown == null)
		{
			return false;
		}
		try
		{
			return (bool)s_getKeyDown.Invoke(null, new object[1] { k });
		}
		catch
		{
			return false;
		}
	}

	public static bool GetKey(KeyCode k)
	{
		//IL_0020: Unknown result type (might be due to invalid IL or missing references)
		if (s_getKey == null)
		{
			return false;
		}
		try
		{
			return (bool)s_getKey.Invoke(null, new object[1] { k });
		}
		catch
		{
			return false;
		}
	}
}