using System; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Security; using System.Security.Permissions; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using HarmonyLib; using Microsoft.CodeAnalysis; using Pigeon; using UnityEngine; using UnityEngine.Events; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: IgnoresAccessChecksTo("Assembly-CSharp")] [assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")] [assembly: AssemblyCompany("Sparroh")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("1.0.1.0")] [assembly: AssemblyInformationalVersion("1.0.1")] [assembly: AssemblyProduct("RandomizerModifiers")] [assembly: AssemblyTitle("RandomizerModifiers")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("1.0.1.0")] [module: UnverifiableCode] [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; } } } public static class ConfigManager { private const float DebounceSeconds = 0.25f; private static ConfigFile config; private static ManualLogSource logger; private static FileSystemWatcher configWatcher; private static volatile bool pendingRefresh; private static volatile bool reloadPending; private static float lastReloadTime; public static ConfigEntry EnableSplitPersonality { get; private set; } public static ConfigEntry EnableButterFingers { get; private set; } public static ConfigEntry SplitPersonalityWeight { get; private set; } public static ConfigEntry ButterFingersWeight { get; private set; } public static ConfigEntry LogModifiersOnLoad { get; private set; } public static void Initialize(ConfigFile configFile, ManualLogSource log) { //IL_006c: Unknown result type (might be due to invalid IL or missing references) //IL_0076: Expected O, but got Unknown //IL_009d: Unknown result type (might be due to invalid IL or missing references) //IL_00a7: Expected O, but got Unknown config = configFile; logger = log; EnableSplitPersonality = config.Bind("General", "Enable Split Personality", true, "Re-enable the Split Personality modifier (randomize employee/character on revive)."); EnableButterFingers = config.Bind("General", "Enable Butter Fingers", true, "Re-enable the Butter Fingers modifier (randomize weapons on revive)."); SplitPersonalityWeight = config.Bind("Weights", "Split Personality Weight", 1, new ConfigDescription("Spawn weight for Split Personality. Vanilla disabled it with 0; 1 matches a normal modifier.", (AcceptableValueBase)(object)new AcceptableValueRange(0, 100), Array.Empty())); ButterFingersWeight = config.Bind("Weights", "Butter Fingers Weight", 1, new ConfigDescription("Spawn weight for Butter Fingers. Vanilla disabled it with 0; 1 matches a normal modifier.", (AcceptableValueBase)(object)new AcceptableValueRange(0, 100), Array.Empty())); LogModifiersOnLoad = config.Bind("Debug", "Log Modifiers On Load", false, "Log every mission modifier API name and weight when Global loads."); EnableSplitPersonality.SettingChanged += OnSettingChanged; EnableButterFingers.SettingChanged += OnSettingChanged; SplitPersonalityWeight.SettingChanged += OnSettingChanged; ButterFingersWeight.SettingChanged += OnSettingChanged; LogModifiersOnLoad.SettingChanged += OnSettingChanged; try { SetupFileWatcher(); } catch (Exception ex) { logger.LogError((object)("Error setting up config file watcher: " + ex.Message)); } } public static void Tick() { if (!reloadPending || Time.unscaledTime - lastReloadTime < 0.25f) { return; } reloadPending = false; lastReloadTime = Time.unscaledTime; try { config.Reload(); pendingRefresh = true; logger.LogInfo((object)"Config reloaded from disk."); } catch (Exception ex) { logger.LogError((object)("Error reloading config: " + ex.Message)); } } public static bool ConsumePendingRefresh() { if (!pendingRefresh) { return false; } pendingRefresh = false; return true; } public static void Dispose() { if (EnableSplitPersonality != null) { EnableSplitPersonality.SettingChanged -= OnSettingChanged; } if (EnableButterFingers != null) { EnableButterFingers.SettingChanged -= OnSettingChanged; } if (SplitPersonalityWeight != null) { SplitPersonalityWeight.SettingChanged -= OnSettingChanged; } if (ButterFingersWeight != null) { ButterFingersWeight.SettingChanged -= OnSettingChanged; } if (LogModifiersOnLoad != null) { LogModifiersOnLoad.SettingChanged -= OnSettingChanged; } if (configWatcher != null) { configWatcher.EnableRaisingEvents = false; configWatcher.Changed -= OnConfigFileChanged; configWatcher.Created -= OnConfigFileChanged; configWatcher.Renamed -= OnConfigFileChanged; configWatcher.Dispose(); configWatcher = null; } } private static void SetupFileWatcher() { configWatcher = new FileSystemWatcher(Paths.ConfigPath, "sparroh.randomizermodifiers.cfg"); configWatcher.NotifyFilter = NotifyFilters.FileName | NotifyFilters.Size | NotifyFilters.LastWrite; configWatcher.Changed += OnConfigFileChanged; configWatcher.Created += OnConfigFileChanged; configWatcher.Renamed += OnConfigFileChanged; configWatcher.EnableRaisingEvents = true; } private static void OnConfigFileChanged(object sender, FileSystemEventArgs e) { reloadPending = true; } private static void OnSettingChanged(object sender, EventArgs e) { pendingRefresh = true; } } [HarmonyPatch(typeof(Global), "LoadInstance")] internal static class GlobalLoadHook { [HarmonyPostfix] private static void Postfix() { ModifierEnabler.TryEnable("Global.LoadInstance"); } } public static class ModifierEnabler { private const string CharacterMethod = "RandomizeCharacterOnRevive"; private const string GearMethod = "RandomizeGearOnRevive"; private static readonly string[] CharacterApiFallbacks = new string[4] { "m_split", "m_personality", "m_char", "split_personality" }; private static readonly string[] GearApiFallbacks = new string[4] { "m_butter", "m_fingers", "m_gear", "butter_fingers" }; private static bool _applied; public static void TryEnable(string reason, bool force = false) { if (_applied && !force) { return; } if ((Object)(object)Global.Instance == (Object)null || Global.Instance.MissionModifiers == null) { RandomizerModifiersPlugin.Logger.LogDebug((object)("[RandomizerModifiers] Global not ready yet (" + reason + ").")); return; } try { EnableRandomizerModifiers(); _applied = true; } catch (Exception arg) { RandomizerModifiersPlugin.Logger.LogError((object)$"[RandomizerModifiers] Failed to enable modifiers ({reason}): {arg}"); } } private static void EnableRandomizerModifiers() { WeightedArray missionModifiers = Global.Instance.MissionModifiers; int length = missionModifiers.Length; if (ConfigManager.LogModifiersOnLoad.Value) { LogAllModifiers(missionModifiers, length); } int num = -1; int num2 = -1; for (int i = 0; i < length; i++) { MissionModifier obj = missionModifiers[i]; MissionModifierGeneric val = (MissionModifierGeneric)(object)((obj is MissionModifierGeneric) ? obj : null); if (val != null && TryGetActionMethodNames(val, out var methods)) { if (num < 0 && methods.Contains("RandomizeCharacterOnRevive")) { num = i; } if (num2 < 0 && methods.Contains("RandomizeGearOnRevive")) { num2 = i; } } } if (num < 0) { num = FindByApiName(missionModifiers, length, CharacterApiFallbacks); } if (num2 < 0) { num2 = FindByApiName(missionModifiers, length, GearApiFallbacks); } bool flag = false; if (ConfigManager.EnableSplitPersonality.Value) { if (num >= 0) { flag |= SetWeight(missionModifiers, num, ConfigManager.SplitPersonalityWeight.Value, "Split Personality"); } else { RandomizerModifiersPlugin.Logger.LogWarning((object)"[RandomizerModifiers] Could not find Split Personality (RandomizeCharacterOnRevive). Enable Debug.LogModifiersOnLoad to inspect the pool."); } } else { RandomizerModifiersPlugin.Logger.LogInfo((object)"[RandomizerModifiers] Split Personality left unchanged (disabled in config)."); } if (ConfigManager.EnableButterFingers.Value) { if (num2 >= 0) { flag |= SetWeight(missionModifiers, num2, ConfigManager.ButterFingersWeight.Value, "Butter Fingers"); } else { RandomizerModifiersPlugin.Logger.LogWarning((object)"[RandomizerModifiers] Could not find Butter Fingers (RandomizeGearOnRevive). Enable Debug.LogModifiersOnLoad to inspect the pool."); } } else { RandomizerModifiersPlugin.Logger.LogInfo((object)"[RandomizerModifiers] Butter Fingers left unchanged (disabled in config)."); } if (flag) { missionModifiers.SetupWeightSum(); RandomizerModifiersPlugin.Logger.LogInfo((object)"[RandomizerModifiers] MissionModifiers weight sum refreshed."); } else { RandomizerModifiersPlugin.Logger.LogInfo((object)"[RandomizerModifiers] No weight changes were required."); } } private static bool SetWeight(WeightedArray pool, int index, int weight, string label) { int weight2 = pool.GetWeight(index); MissionModifier val = pool[index]; string text = (((Object)(object)val != (Object)null) ? val.APIName : "?"); if (weight2 == weight) { RandomizerModifiersPlugin.Logger.LogInfo((object)$"[RandomizerModifiers] {label} ('{text}') already has weight {weight} (index {index})."); return false; } pool.SetWeight(index, weight); RandomizerModifiersPlugin.Logger.LogInfo((object)$"[RandomizerModifiers] {label} ('{text}') weight {weight2} -> {weight} (index {index})."); return true; } private static int FindByApiName(WeightedArray pool, int length, string[] candidates) { for (int i = 0; i < length; i++) { MissionModifier val = pool[i]; if ((Object)(object)val == (Object)null || string.IsNullOrEmpty(val.APIName)) { continue; } string aPIName = val.APIName; for (int j = 0; j < candidates.Length; j++) { if (aPIName.IndexOf(candidates[j], StringComparison.OrdinalIgnoreCase) >= 0) { return i; } } } return -1; } private static bool TryGetActionMethodNames(MissionModifierGeneric generic, out HashSet methods) { methods = new HashSet(StringComparer.Ordinal); FieldInfo field = typeof(MissionModifierGeneric).GetField("action", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); if (field == null) { return false; } object? value = field.GetValue(generic); UnityEventBase val = (UnityEventBase)((value is UnityEventBase) ? value : null); if (val == null) { return false; } int persistentEventCount = val.GetPersistentEventCount(); for (int i = 0; i < persistentEventCount; i++) { string persistentMethodName = val.GetPersistentMethodName(i); if (!string.IsNullOrEmpty(persistentMethodName)) { methods.Add(persistentMethodName); } } return methods.Count > 0; } private static void LogAllModifiers(WeightedArray pool, int length) { //IL_00cb: Unknown result type (might be due to invalid IL or missing references) RandomizerModifiersPlugin.Logger.LogInfo((object)$"[RandomizerModifiers] Dumping {length} mission modifiers:"); for (int i = 0; i < length; i++) { MissionModifier val = pool[i]; if ((Object)(object)val == (Object)null) { RandomizerModifiersPlugin.Logger.LogInfo((object)$" [{i}] weight={pool.GetWeight(i)}"); continue; } string arg = ""; MissionModifierGeneric val2 = (MissionModifierGeneric)(object)((val is MissionModifierGeneric) ? val : null); if (val2 != null && TryGetActionMethodNames(val2, out var methods)) { arg = " methods=[" + string.Join(", ", methods) + "]"; } RandomizerModifiersPlugin.Logger.LogInfo((object)($" [{i}] api='{val.APIName}' type={((object)val).GetType().Name} " + $"weight={pool.GetWeight(i)} flags={val.Flags}{arg}")); } } } [BepInPlugin("sparroh.randomizermodifiers", "RandomizerModifiers", "1.0.1")] [MycoMod(/*Could not decode attribute arguments.*/)] public class RandomizerModifiersPlugin : BaseUnityPlugin { public const string PluginGUID = "sparroh.randomizermodifiers"; public const string PluginName = "RandomizerModifiers"; public const string PluginVersion = "1.0.1"; internal static ManualLogSource Logger; internal static RandomizerModifiersPlugin Instance; private Harmony _harmony; private void Awake() { //IL_0027: Unknown result type (might be due to invalid IL or missing references) //IL_0031: Expected O, but got Unknown Instance = this; Logger = ((BaseUnityPlugin)this).Logger; ConfigManager.Initialize(((BaseUnityPlugin)this).Config, Logger); _harmony = new Harmony("sparroh.randomizermodifiers"); try { _harmony.PatchAll(typeof(GlobalLoadHook)); Logger.LogInfo((object)"Harmony patches applied."); } catch (Exception ex) { Logger.LogError((object)("Error applying patches: " + ex.Message)); } ModifierEnabler.TryEnable("Awake"); Logger.LogInfo((object)"RandomizerModifiers v1.0.1 loaded."); } private void Update() { ConfigManager.Tick(); if (ConfigManager.ConsumePendingRefresh()) { ModifierEnabler.TryEnable("ConfigChanged", force: true); } } private void OnDestroy() { ConfigManager.Dispose(); Harmony harmony = _harmony; if (harmony != null) { harmony.UnpatchSelf(); } _harmony = null; Instance = null; } } namespace RandomizerModifiers { public static class MyPluginInfo { public const string PLUGIN_GUID = "RandomizerModifiers"; public const string PLUGIN_NAME = "RandomizerModifiers"; public const string PLUGIN_VERSION = "1.0.1"; } } namespace System.Runtime.CompilerServices { [AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)] internal sealed class IgnoresAccessChecksToAttribute : Attribute { public IgnoresAccessChecksToAttribute(string assemblyName) { } } }