using System; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using BepInEx; using HarmonyLib; using UnityEngine; using UnityEngine.UI; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)] [assembly: AssemblyTitle("TwoRelics")] [assembly: AssemblyDescription("")] [assembly: AssemblyConfiguration("")] [assembly: AssemblyCompany("")] [assembly: AssemblyProduct("TwoRelics")] [assembly: AssemblyCopyright("Copyright © 2026")] [assembly: AssemblyTrademark("")] [assembly: ComVisible(false)] [assembly: Guid("880eb189-aa99-447c-aed4-d980ee9251c9")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: AssemblyVersion("1.0.0.0")] [BepInPlugin("com.mozam.tworelics", "Two Relics Mod", "1.0.0")] public class TwoRelicsPlugin : BaseUnityPlugin { public static Dictionary secondRelicDict = new Dictionary(); private Harmony harmony; private void Awake() { //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_0011: Expected O, but got Unknown harmony = new Harmony("com.mozam.tworelics"); harmony.PatchAll(); ((BaseUnityPlugin)this).Logger.LogInfo((object)"Two Relics Mod loaded - patches applied."); } } [HarmonyPatch(typeof(RelicChestUI), "Update")] public static class RelicChestUI_Update_SecondRelicKey_Patch { private static readonly FieldInfo currentPlayerField = typeof(RelicChestUI).GetField("currentPlayer", BindingFlags.Instance | BindingFlags.NonPublic); private static readonly FieldInfo currentFocusField = typeof(RelicChestUI).GetField("currentFocus", BindingFlags.Instance | BindingFlags.NonPublic); private static readonly FieldInfo currentSelectionListField = typeof(RelicChestUI).GetField("currentSelectionList", BindingFlags.Instance | BindingFlags.NonPublic); private static readonly FieldInfo currentStartIndexField = typeof(RelicChestUI).GetField("currentStartIndex", BindingFlags.Instance | BindingFlags.NonPublic); private static readonly FieldInfo currentSelectionIndexField = typeof(RelicChestUI).GetField("currentSelectionIndex", BindingFlags.Instance | BindingFlags.NonPublic); private static readonly FieldInfo currentIDField = typeof(RelicChestUI).GetField("currentID", BindingFlags.Instance | BindingFlags.NonPublic); private static readonly FieldInfo activationPositionField = typeof(RelicChestUI).GetField("activationPosition", BindingFlags.Instance | BindingFlags.NonPublic); private static readonly FieldInfo maxDistanceField = typeof(RelicChestUI).GetField("maxDistance", BindingFlags.Instance | BindingFlags.NonPublic); private static void Postfix(RelicChestUI __instance) { //IL_002d: Unknown result type (might be due to invalid IL or missing references) //IL_0033: Expected O, but got Unknown //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_006e: Unknown result type (might be due to invalid IL or missing references) //IL_0073: Unknown result type (might be due to invalid IL or missing references) //IL_0078: Unknown result type (might be due to invalid IL or missing references) //IL_00be: Unknown result type (might be due to invalid IL or missing references) //IL_00c3: Unknown result type (might be due to invalid IL or missing references) //IL_00c4: Unknown result type (might be due to invalid IL or missing references) //IL_00c6: Invalid comparison between Unknown and I4 if (!__instance.isActive || GameUI.pauseMenu.paused) { return; } Player val = (Player)currentPlayerField.GetValue(__instance); if ((Object)(object)val == (Object)null) { return; } Vector2 val2 = (Vector2)activationPositionField.GetValue(__instance); float num = (float)maxDistanceField.GetValue(__instance); if (Vector2.Distance(Vector2.op_Implicit(((Component)val).transform.position), val2) > num || PlayerUIBase.GetLock("RelicChestUI" + val.playerID)) { return; } RCFocus val3 = (RCFocus)currentFocusField.GetValue(__instance); if ((int)val3 != 1 || !Input.GetKeyDown((KeyCode)50)) { return; } List list = (List)currentSelectionListField.GetValue(__instance); int num2 = (int)currentStartIndexField.GetValue(__instance); int num3 = (int)currentSelectionIndexField.GetValue(__instance); string text = list[num2 + num3]; if (!Item.IsUnlocked(text, false)) { SoundManager.PlayAudio("MenuError", 1f, false, -1f, -1f); return; } int playerID = val.playerID; if (TwoRelicsPlugin.secondRelicDict.TryGetValue(playerID, out var value) && !string.IsNullOrEmpty(value)) { ((Entity)val).inventory.RemoveItem(value, false, false); } TwoRelicsPlugin.secondRelicDict[playerID] = text; TwoRelicsSaveData.Save(); ((Entity)val).inventory.AddItem(text, false, false); GameDataManager.Save(true, true); ((Entity)val).health.RestoreHealth(9999, false, false, true, true); currentIDField.SetValue(__instance, text); SoundManager.PlayAudio("WoodPickUp", 1f, false, -1f, -1f); __instance.SetFocus((RCFocus)0, false, false); } } internal class SecondRelicUIRefs { public Image icon; public Text text; } internal static class SecondRelicUI { private static readonly Dictionary cache = new Dictionary(); private static readonly FieldInfo rcRefField = typeof(RelicChestUI).GetField("rcRef", BindingFlags.Instance | BindingFlags.NonPublic); private static readonly Type rcObjRefType = typeof(RelicChestUI).GetNestedType("RelicChestObjRef", BindingFlags.NonPublic); private static readonly FieldInfo typePageTransField = rcObjRefType?.GetField("typePageTrans"); private static readonly Vector2 secondRelicOffset = new Vector2(0f, -40f); public static SecondRelicUIRefs GetOrCreate(RelicChestUI instance) { //IL_0034: Unknown result type (might be due to invalid IL or missing references) //IL_003a: Expected O, but got Unknown //IL_007c: Unknown result type (might be due to invalid IL or missing references) //IL_0081: Unknown result type (might be due to invalid IL or missing references) //IL_0086: Unknown result type (might be due to invalid IL or missing references) if (cache.TryGetValue(instance, out var value)) { return value; } object value2 = rcRefField.GetValue(instance); Transform val = (Transform)typePageTransField.GetValue(value2); Transform val2 = val.Find("CurrentRelic"); GameObject val3 = Object.Instantiate(((Component)val2).gameObject, val); ((Object)val3).name = "CurrentRelic (Second)"; RectTransform component = val3.GetComponent(); if ((Object)(object)component != (Object)null) { component.anchoredPosition += secondRelicOffset; } DisableChildIfPresent(val3.transform, "ConfirmIcon"); DisableChildIfPresent(val3.transform, "CancelIcon"); DisableChildIfPresent(val3.transform, "ConfirmText"); DisableChildIfPresent(val3.transform, "CancelText"); Transform val4 = val3.transform.Find("Icon"); Transform val5 = val3.transform.Find("Text"); SecondRelicUIRefs secondRelicUIRefs = new SecondRelicUIRefs { icon = ((Component)val4).GetComponent(), text = ((Component)val5).GetComponent() }; cache.Add(instance, secondRelicUIRefs); return secondRelicUIRefs; } private static void DisableChildIfPresent(Transform parent, string childName) { Transform val = parent.Find(childName); if ((Object)(object)val != (Object)null) { ((Component)val).gameObject.SetActive(false); } } } internal static class SlotHintUI { private static readonly Dictionary created = new Dictionary(); private static readonly FieldInfo rcRefField = typeof(RelicChestUI).GetField("rcRef", BindingFlags.Instance | BindingFlags.NonPublic); private static readonly Type rcObjRefType = typeof(RelicChestUI).GetNestedType("RelicChestObjRef", BindingFlags.NonPublic); private static readonly FieldInfo selectPageTransField = rcObjRefType?.GetField("selectPageTrans"); private static readonly Vector2 hintOffset = new Vector2(0f, -25f); public static void EnsureCreated(RelicChestUI instance) { //IL_002f: Unknown result type (might be due to invalid IL or missing references) //IL_0035: Expected O, but got Unknown //IL_0079: Unknown result type (might be due to invalid IL or missing references) //IL_007e: Unknown result type (might be due to invalid IL or missing references) //IL_0083: Unknown result type (might be due to invalid IL or missing references) if (!created.ContainsKey(instance)) { object value = rcRefField.GetValue(instance); Transform val = (Transform)selectPageTransField.GetValue(value); Transform val2 = val.Find("Info/BGImage/ConfirmText"); GameObject val3 = Object.Instantiate(((Component)val2).gameObject, val2.parent); ((Object)val3).name = "SlotTwoHintText"; RectTransform component = val3.GetComponent(); if ((Object)(object)component != (Object)null) { component.anchoredPosition += hintOffset; } Text component2 = val3.GetComponent(); if ((Object)(object)component2 != (Object)null) { component2.text = "2: Equip Slot 2"; } created[instance] = true; } } } [HarmonyPatch(typeof(RelicChestUI), "LoadSelectionPage")] public static class RelicChestUI_LoadSelectionPage_SlotHint_Patch { private static void Postfix(RelicChestUI __instance) { SlotHintUI.EnsureCreated(__instance); } } [HarmonyPatch(typeof(RelicChestUI), "LoadTypePage")] public static class RelicChestUI_LoadTypePage_SecondRelicDisplay_Patch { private static readonly FieldInfo currentPlayerField = typeof(RelicChestUI).GetField("currentPlayer", BindingFlags.Instance | BindingFlags.NonPublic); private static void Postfix(RelicChestUI __instance) { //IL_003d: Unknown result type (might be due to invalid IL or missing references) //IL_0043: Expected O, but got Unknown SecondRelicUIRefs orCreate = SecondRelicUI.GetOrCreate(__instance); if (orCreate == null || (Object)(object)orCreate.icon == (Object)null || (Object)(object)orCreate.text == (Object)null) { return; } Player val = (Player)currentPlayerField.GetValue(__instance); if (!((Object)(object)val == (Object)null)) { TwoRelicsPlugin.secondRelicDict.TryGetValue(val.playerID, out var value); if (string.IsNullOrEmpty(value)) { ((Behaviour)orCreate.icon).enabled = false; orCreate.text.text = string.Empty; } else { ((Behaviour)orCreate.icon).enabled = true; orCreate.icon.sprite = IconManager.GetItemIcon(value); orCreate.text.text = TextManager.GetItemName(value); } } } } [HarmonyPatch(typeof(Player), "Start")] public static class Player_Start_RestoreSecondRelic_Patch { private static void Postfix(Player __instance) { if (!Globals.IsMuseum && !Globals.IsPvPScene && TwoRelicsPlugin.secondRelicDict.TryGetValue(__instance.playerID, out var value) && !string.IsNullOrEmpty(value)) { ((Entity)__instance).inventory.AddItem(value, false, false); } } } [HarmonyPatch(typeof(GameDataManager), "LoadInitial")] public static class GameDataManager_LoadInitial_Patch { private static void Postfix() { TwoRelicsSaveData.Load(); } } [HarmonyPatch(typeof(GameDataManager), "ResetAllData")] public static class GameDataManager_ResetAllData_Patch { private static void Postfix() { TwoRelicsSaveData.DeleteAll(); } } internal static class TwoRelicsSaveData { private const string fileName = "TwoRelicsData.txt"; private static string FilePath => Application.persistentDataPath + "/" + GameDataManager.steamIDStr + "TwoRelicsData.txt"; public static void Save() { try { List list = new List(); foreach (KeyValuePair item in TwoRelicsPlugin.secondRelicDict) { if (!string.IsNullOrEmpty(item.Value)) { list.Add(item.Key + "|" + item.Value); } } File.WriteAllLines(FilePath, list.ToArray()); } catch (Exception ex) { Debug.LogWarning((object)("[TwoRelics] Failed to save second relic data: " + ex)); } } public static void Load() { try { TwoRelicsPlugin.secondRelicDict.Clear(); if (!File.Exists(FilePath)) { return; } string[] array = File.ReadAllLines(FilePath); foreach (string text in array) { string[] array2 = text.Split(new char[1] { '|' }); if (array2.Length == 2 && int.TryParse(array2[0], out var result)) { TwoRelicsPlugin.secondRelicDict[result] = array2[1]; } } } catch (Exception ex) { Debug.LogWarning((object)("[TwoRelics] Failed to load second relic data: " + ex)); } } public static void DeleteAll() { try { TwoRelicsPlugin.secondRelicDict.Clear(); if (File.Exists(FilePath)) { File.Delete(FilePath); } } catch (Exception ex) { Debug.LogWarning((object)("[TwoRelics] Failed to delete second relic data: " + ex)); } } }