using BepInEx; using BepInEx.Logging; using HarmonyLib; using System; using System.Linq; namespace DevourUnlocker.BepInEx { [BepInPlugin("com.mithral.devourunlocker", "DevourUnlocker", "2.0.0")] public class Plugin : BaseUnityPlugin { internal static ManualLogSource Log; private Harmony _harmony; void Awake() { Log = Logger; Log.LogInfo("DevourUnlocker BepInEx v2 loaded — selective unlock"); _harmony = new Harmony("com.mithral.devourunlocker"); try { _harmony.PatchAll(); Log.LogInfo("Harmony patches applied"); } catch (Exception e) { Log.LogError(e); } } void OnGUI() { if (!DevourGUI.visible) return; DevourGUI.Draw(); } void Update() { if (UnityEngine.Input.GetKeyDown(UnityEngine.KeyCode.F6)) DevourGUI.visible = !DevourGUI.visible; } void OnDestroy() { _harmony?.UnpatchSelf(); } } // Generic unlock patches — try multiple owner classes static class Patches { // Use TargetMethod to handle both old and new class names [HarmonyPatch] class IsRobeUnlockedPatch { static System.Reflection.MethodBase TargetMethod() { var types = new[] { "NolanRankController", "LobbyController", "InventoryController", "UnlockManager" }; foreach (var t in types) { var type = AccessTools.TypeByName(t); if (type == null) continue; var m = AccessTools.Method(type, "IsRobeUnlocked"); if (m != null) { Plugin.Log?.LogInfo($"Found IsRobeUnlocked on {t}"); return m; } } Plugin.Log?.LogWarning("IsRobeUnlocked not found — will retry via standalone"); return null; } static bool Prefix(ref bool __result, object __instance, string robeName) { if (!DevourGUI.configUnlockRobes) return true; // run original if (DevourGUI.robeOverrides.TryGetValue(robeName ?? "", out var v)) { __result = v; return false; } __result = true; if (!string.IsNullOrEmpty(robeName)) Plugin.Log?.LogInfo($"[Robe] {robeName} -> unlocked"); return false; } } [HarmonyPatch] class IsCharacterUnlockedPatch { static System.Reflection.MethodBase TargetMethod() { var types = new[] { "NolanRankController", "LobbyController", "CharacterManager" }; foreach (var t in types) { var type = AccessTools.TypeByName(t); if (type == null) continue; var m = AccessTools.Method(type, "IsCharacterUnlocked"); if (m != null) return m; } return null; } static bool Prefix(ref bool __result, string prefabName) { if (!DevourGUI.configUnlockChars) return true; if (DevourGUI.charOverrides.TryGetValue(prefabName ?? "", out var v)) { __result = v; return false; } __result = true; return false; } } } static class DevourGUI { public static bool visible = true; public static bool configUnlockRobes = true; public static bool configUnlockChars = true; public static System.Collections.Generic.Dictionary robeOverrides = new(); public static System.Collections.Generic.Dictionary charOverrides = new(); static string[] robes = new[] { "StraightJacket","Patchwork","CherryBlossom","Eggs","Poison","Web", "TownHard","TownNightmare","TownBooks","Horseshoes", "SlaughterhouseHard","SlaughterhouseNightmare","SlaughterhousePigs","BarbedWires", "Golden","ManorHard","ManorNightmare","ManorHeadsBuried","Feathers", "CarnivalHard","CarnivalNightmare","CarnivalDollHeads","Tickets" }; static string[] chars = new[] {"Molly","Nathan","Sam","Zara","Cultist","Kai","Anna","April","Frank"}; static UnityEngine.Vector2 scroll; public static void Draw() { var rect = new UnityEngine.Rect(20, 20, 520, 600); UnityEngine.GUI.Box(rect, ""); GUILayout.BeginArea(rect); GUILayout.Label("DevourUnlocker v2 — BepInEx Edition (F6 toggle)"); GUILayout.Label("Multiplayer safe — client visual unlock"); configUnlockRobes = GUILayout.Toggle(configUnlockRobes, "Unlock Robes"); configUnlockChars = GUILayout.Toggle(configUnlockChars, "Unlock Characters"); if (GUILayout.Button("Unlock All")) { configUnlockRobes = configUnlockChars = true; robeOverrides.Clear(); charOverrides.Clear(); } if (GUILayout.Button("Lock All")) { configUnlockRobes = configUnlockChars = false; } GUILayout.Label("— Robes —"); scroll = GUILayout.BeginScrollView(scroll, GUILayout.Height(220)); foreach (var r in robes) { bool cur = robeOverrides.TryGetValue(r, out var ov) ? ov : configUnlockRobes; bool nxt = GUILayout.Toggle(cur, r); if (nxt != cur) robeOverrides[r] = nxt; } GUILayout.EndScrollView(); GUILayout.Label("— Characters —"); foreach (var c in chars) { bool cur = charOverrides.TryGetValue(c, out var ov) ? ov : configUnlockChars; bool nxt = GUILayout.Toggle(cur, c); if (nxt != cur) charOverrides[c] = nxt; } GUILayout.EndArea(); } } }