using System; using System.Collections.Generic; using System.Diagnostics; using System.Globalization; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Text; using BepInEx; using BepInEx.Bootstrap; using BepInEx.Configuration; using BepInEx.Logging; using GameNetcodeStuff; using HarmonyLib; using Microsoft.CodeAnalysis; using Unity.Collections; using Unity.Netcode; using UnityEngine; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")] [assembly: AssemblyCompany("ExhaustedBatteries")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: AssemblyInformationalVersion("1.0.0+fe06e0258f1397ec38ac6c6dfe6b28fe265d83ae")] [assembly: AssemblyProduct("Exhausted Batteries")] [assembly: AssemblyTitle("ExhaustedBatteries")] [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 ExhaustedBatteries { internal static class Loc { internal enum Language { Auto, English, Russian } private static bool resolved; private static bool russian; private static bool IsRussian { get { if (!resolved) { Resolve(); } return russian; } } internal static string ChargerTipHeader { get { if (!IsRussian) { return "Charging Station"; } return "Зарядная станция"; } } internal static string NoChargesLeft { get { if (!IsRussian) { return "No charges left" + (Plugin.ResetChargesEachDay.Value ? " for today." : "."); } if (!Plugin.ResetChargesEachDay.Value) { return "Заряды закончились."; } return "Заряды на сегодня закончились."; } } internal static void Invalidate() { resolved = false; } private static void Resolve() { resolved = true; switch ((Plugin.LanguageSetting != null) ? Plugin.LanguageSetting.Value : Language.Auto) { case Language.English: russian = false; break; case Language.Russian: russian = true; break; default: russian = DetectRussian(); break; } } private static bool DetectRussian() { //IL_00a2: Unknown result type (might be due to invalid IL or missing references) //IL_00a9: Invalid comparison between Unknown and I4 try { foreach (KeyValuePair pluginInfo in Chainloader.PluginInfos) { string s = ((pluginInfo.Key != null) ? pluginInfo.Key.ToLowerInvariant() : ""); PluginInfo value = pluginInfo.Value; object obj; if (value == null) { obj = null; } else { BepInPlugin metadata = value.Metadata; obj = ((metadata != null) ? metadata.Name : null); } string s2 = ((obj != null) ? pluginInfo.Value.Metadata.Name.ToLowerInvariant() : ""); if (Marks(s) || Marks(s2)) { return true; } } } catch { } try { if ((int)Application.systemLanguage == 30) { return true; } } catch { } return false; } private static bool Marks(string s) { if (!s.Contains("rtlc") && !s.Contains("rulethal") && !s.Contains("russian") && !s.Contains("русск")) { return s.Contains("russiantranslation"); } return true; } } internal static class ConfigSync { [CompilerGenerated] private static class <>O { public static HandleNamedMessageDelegate <0>__OnHostReceivedRequest; public static HandleNamedMessageDelegate <1>__OnClientReceivedConfig; } private const string RequestMsg = "ExhaustedBatteries_RequestConfig"; private const string ReceiveMsg = "ExhaustedBatteries_ReceiveConfig"; private static byte[] localBlob; private static byte[] hostBlob; internal static bool IsActive { get; private set; } internal static bool Applying { get; private set; } internal static bool IsSyncedClient => localBlob != null; internal static void OnLocalConnect() { RevertToLocal(); NetworkManager singleton = NetworkManager.Singleton; if (!((Object)(object)singleton == (Object)null)) { if (singleton.IsServer) { SetupHost(); } else { SetupClient(); } } } private static void SetupHost() { //IL_0025: Unknown result type (might be due to invalid IL or missing references) //IL_002a: Unknown result type (might be due to invalid IL or missing references) //IL_0030: Expected O, but got Unknown CustomMessagingManager customMessagingManager = NetworkManager.Singleton.CustomMessagingManager; SafeUnregister(customMessagingManager); object obj = <>O.<0>__OnHostReceivedRequest; if (obj == null) { HandleNamedMessageDelegate val = OnHostReceivedRequest; <>O.<0>__OnHostReceivedRequest = val; obj = (object)val; } customMessagingManager.RegisterNamedMessageHandler("ExhaustedBatteries_RequestConfig", (HandleNamedMessageDelegate)obj); IsActive = true; Plugin.Log.LogInfo((object)"[Sync] Host: local config is authoritative."); Plugin.ReapplyAll(); } private unsafe static void SetupClient() { //IL_0027: Unknown result type (might be due to invalid IL or missing references) //IL_002c: Unknown result type (might be due to invalid IL or missing references) //IL_0032: Expected O, but got Unknown //IL_004f: Unknown result type (might be due to invalid IL or missing references) CustomMessagingManager customMessagingManager = NetworkManager.Singleton.CustomMessagingManager; SafeUnregister(customMessagingManager); object obj = <>O.<1>__OnClientReceivedConfig; if (obj == null) { HandleNamedMessageDelegate val = OnClientReceivedConfig; <>O.<1>__OnClientReceivedConfig = val; obj = (object)val; } customMessagingManager.RegisterNamedMessageHandler("ExhaustedBatteries_ReceiveConfig", (HandleNamedMessageDelegate)obj); IsActive = false; FastBufferWriter val2 = default(FastBufferWriter); ((FastBufferWriter)(ref val2))..ctor(4, (Allocator)2, -1); try { customMessagingManager.SendNamedMessage("ExhaustedBatteries_RequestConfig", 0uL, val2, (NetworkDelivery)4); Plugin.Log.LogInfo((object)"[Sync] Client: requested host config."); } finally { ((IDisposable)(*(FastBufferWriter*)(&val2))/*cast due to .constrained prefix*/).Dispose(); } } internal static void RevertToLocal() { SafeUnregister(((Object)(object)NetworkManager.Singleton != (Object)null) ? NetworkManager.Singleton.CustomMessagingManager : null); if (localBlob != null) { Apply(localBlob); localBlob = null; } hostBlob = null; IsActive = false; } internal static void EnforceHostAuthority() { if (IsSyncedClient && hostBlob != null) { Apply(hostBlob); } } private unsafe static void OnHostReceivedRequest(ulong clientId, FastBufferReader reader) { //IL_0036: Unknown result type (might be due to invalid IL or missing references) //IL_003c: 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) if ((Object)(object)NetworkManager.Singleton == (Object)null || !NetworkManager.Singleton.IsServer) { return; } byte[] array = Serialize(); int num = array.Length; FastBufferWriter val = default(FastBufferWriter); ((FastBufferWriter)(ref val))..ctor(num + 4, (Allocator)2, -1); try { ((FastBufferWriter)(ref val)).WriteValueSafe(ref num, default(ForPrimitives)); ((FastBufferWriter)(ref val)).WriteBytesSafe(array, num, 0); NetworkManager.Singleton.CustomMessagingManager.SendNamedMessage("ExhaustedBatteries_ReceiveConfig", clientId, val, (NetworkDelivery)4); Plugin.Log.LogInfo((object)$"[Sync] Host: sent config to client {clientId} ({num} bytes)."); } finally { ((IDisposable)(*(FastBufferWriter*)(&val))/*cast due to .constrained prefix*/).Dispose(); } } private static void OnClientReceivedConfig(ulong _, FastBufferReader reader) { //IL_0015: Unknown result type (might be due to invalid IL or missing references) //IL_001b: Unknown result type (might be due to invalid IL or missing references) try { if (!((FastBufferReader)(ref reader)).TryBeginRead(4)) { return; } int num = default(int); ((FastBufferReader)(ref reader)).ReadValueSafe(ref num, default(ForPrimitives)); if (num >= 0 && ((FastBufferReader)(ref reader)).TryBeginRead(num)) { byte[] data = new byte[num]; ((FastBufferReader)(ref reader)).ReadBytesSafe(ref data, num, 0); if (localBlob == null) { localBlob = Serialize(); } hostBlob = data; IsActive = true; Apply(data); Plugin.ReapplyAll(); Plugin.Log.LogInfo((object)"[Sync] Client: applied host config."); } } catch (Exception arg) { Plugin.Log.LogError((object)$"[Sync] receive error: {arg}"); } } private static void SafeUnregister(CustomMessagingManager cmm) { if (cmm == null) { return; } try { cmm.UnregisterNamedMessageHandler("ExhaustedBatteries_RequestConfig"); } catch { } try { cmm.UnregisterNamedMessageHandler("ExhaustedBatteries_ReceiveConfig"); } catch { } } private static string F(float v) { return v.ToString("R", CultureInfo.InvariantCulture); } private static byte[] Serialize() { StringBuilder stringBuilder = new StringBuilder(); stringBuilder.Append("gm=").Append(F(Plugin.GlobalMultiplier.Value)).Append('\n'); stringBuilder.Append("ei=").Append(Plugin.EnableIndividualMultipliers.Value).Append('\n'); stringBuilder.Append("sd=").Append(Plugin.SpawnStoreItemsDischarged.Value).Append('\n'); stringBuilder.Append("ic=").Append(F(Plugin.InitialChargeMultiplier.Value)).Append('\n'); stringBuilder.Append("mc=").Append(Plugin.MaxCharges.Value.ToString(CultureInfo.InvariantCulture)).Append('\n'); stringBuilder.Append("rd=").Append(Plugin.ResetChargesEachDay.Value).Append('\n'); stringBuilder.Append("fd=").Append(Plugin.EnableFlashlightDimming.Value).Append('\n'); stringBuilder.Append("ds=").Append(F(Plugin.DimStartCharge.Value)).Append('\n'); stringBuilder.Append("mb=").Append(F(Plugin.MinBrightness.Value)).Append('\n'); foreach (ConfigEntry itemEntry in Plugin.ItemEntries) { stringBuilder.Append("i:").Append(((ConfigEntryBase)itemEntry).Definition.Key).Append('=') .Append(F(itemEntry.Value)) .Append('\n'); } return Encoding.UTF8.GetBytes(stringBuilder.ToString()); } private static void Apply(byte[] data) { Applying = true; bool saveOnConfigSet = Plugin.Cfg.SaveOnConfigSet; Plugin.Cfg.SaveOnConfigSet = false; try { string[] array = Encoding.UTF8.GetString(data).Split('\n'); for (int i = 0; i < array.Length; i++) { string text = array[i].Trim(); if (text.Length != 0) { int num = text.IndexOf('='); if (num > 0) { ApplyOne(text.Substring(0, num), text.Substring(num + 1)); } } } } catch (Exception arg) { Plugin.Log.LogError((object)$"[Sync] apply error: {arg}"); } finally { Plugin.Cfg.SaveOnConfigSet = saveOnConfigSet; Applying = false; } } private static void ApplyOne(string key, string val) { if (key.StartsWith("i:", StringComparison.Ordinal)) { string text = key.Substring(2); Plugin.EnsureIndividualEntry(text); ConfigEntry val2 = Plugin.FindIndividual(text); if (val2 != null && TryF(val, out var f)) { val2.Value = f; } } else { if (key == null) { return; } int length = key.Length; if (length != 2) { return; } switch (key[0]) { case 'g': { if (key == "gm" && TryF(val, out var f5)) { Plugin.GlobalMultiplier.Value = f5; } break; } case 'e': { if (key == "ei" && bool.TryParse(val, out var result4)) { Plugin.EnableIndividualMultipliers.Value = result4; } break; } case 's': { if (key == "sd" && bool.TryParse(val, out var result3)) { Plugin.SpawnStoreItemsDischarged.Value = result3; } break; } case 'i': { if (key == "ic" && TryF(val, out var f3)) { Plugin.InitialChargeMultiplier.Value = f3; } break; } case 'm': { int result; if (!(key == "mc")) { if (key == "mb" && TryF(val, out var f4)) { Plugin.MinBrightness.Value = f4; } } else if (int.TryParse(val, NumberStyles.Integer, CultureInfo.InvariantCulture, out result)) { Plugin.MaxCharges.Value = result; } break; } case 'r': { if (key == "rd" && bool.TryParse(val, out var result5)) { Plugin.ResetChargesEachDay.Value = result5; } break; } case 'f': { if (key == "fd" && bool.TryParse(val, out var result2)) { Plugin.EnableFlashlightDimming.Value = result2; } break; } case 'd': { if (key == "ds" && TryF(val, out var f2)) { Plugin.DimStartCharge.Value = f2; } break; } } } } private static bool TryF(string s, out float f) { return float.TryParse(s, NumberStyles.Float, CultureInfo.InvariantCulture, out f); } } [HarmonyPatch(typeof(PlayerControllerB), "ConnectClientToPlayerObject")] internal static class ConfigSyncConnectPatch { [HarmonyPostfix] private static void Postfix() { ConfigSync.OnLocalConnect(); } } [HarmonyPatch(typeof(FlashlightItem), "Update")] internal static class FlashlightDimPatch { private static readonly Dictionary baseIntensities = new Dictionary(); private static bool loggedOnce; [HarmonyPostfix] [HarmonyPriority(200)] private static void Postfix(FlashlightItem __instance) { if (!ConfigSync.IsActive || !Plugin.EnableFlashlightDimming.Value) { return; } Battery insertedBattery = ((GrabbableObject)__instance).insertedBattery; if (insertedBattery != null) { float num = Brightness(insertedBattery.charge); Apply(__instance.flashlightBulb, num); Apply(__instance.flashlightBulbGlow, num); if (Plugin.VerboseLogging.Value && !loggedOnce) { loggedOnce = true; float num2 = (((Object)(object)__instance.flashlightBulb != (Object)null) ? __instance.flashlightBulb.intensity : (-1f)); Plugin.Log.LogInfo((object)($"[Flashlight] dimming active: charge={insertedBattery.charge:0.##}, " + $"factor={num:0.##}, bulb.intensity={num2:0.##}")); } } } private static void Apply(Light light, float factor) { if (!((Object)(object)light == (Object)null)) { if (!baseIntensities.TryGetValue(light, out var value) || light.intensity > value) { value = light.intensity; baseIntensities[light] = value; } light.intensity = value * factor; } } private static float Brightness(float charge) { float value = Plugin.DimStartCharge.Value; float value2 = Plugin.MinBrightness.Value; charge = Mathf.Clamp01(charge); if (value <= 0f) { return 1f; } if (charge >= value) { return 1f; } return Mathf.Lerp(value2, 1f, charge / value); } internal static void OnSettingsChanged() { if (Plugin.EnableFlashlightDimming.Value) { return; } foreach (KeyValuePair baseIntensity in baseIntensities) { if ((Object)(object)baseIntensity.Key != (Object)null) { baseIntensity.Key.intensity = baseIntensity.Value; } } } } [HarmonyPatch(typeof(GrabbableObject), "Start")] internal static class GrabbableObjectPatch { private static readonly Dictionary baseUsages = new Dictionary(); private static bool reapplying; [HarmonyPostfix] [HarmonyPriority(200)] private static void Postfix(GrabbableObject __instance) { try { if (!ConfigSync.IsActive) { return; } Item itemProperties = __instance.itemProperties; if ((Object)(object)itemProperties == (Object)null || !itemProperties.requiresBattery || __instance.insertedBattery == null || itemProperties.batteryUsage <= 0f) { return; } string usedKey; bool individual; float num = ResolveMultiplier(__instance, itemProperties, out usedKey, out individual); float num2 = ApplyCapacity(itemProperties, num); Battery insertedBattery = __instance.insertedBattery; bool flag = false; if (!itemProperties.isScrap && Plugin.SpawnStoreItemsDischarged.Value) { insertedBattery.charge = 0f; insertedBattery.empty = true; flag = true; } else { float value = Plugin.InitialChargeMultiplier.Value; if (Math.Abs(value - 1f) > 0.0001f) { insertedBattery.charge = Mathf.Clamp01(insertedBattery.charge * value); insertedBattery.empty = insertedBattery.charge <= 0f; } } if (Plugin.VerboseLogging.Value) { Plugin.Log.LogInfo((object)($"[Battery] {Describe(__instance, itemProperties)} | x{num:0.###} " + "(" + (individual ? ("individual:" + usedKey) : "global") + ") | " + $"usage {num2:0.#}s -> {itemProperties.batteryUsage:0.#}s | " + string.Format("charge={0:0.##}{1}", insertedBattery.charge, flag ? " (discharged)" : ""))); } } catch (Exception arg) { Plugin.Log.LogError((object)$"Error applying battery settings: {arg}"); } } private static float ApplyCapacity(Item item, float mult) { if (!baseUsages.TryGetValue(item, out var value)) { value = item.batteryUsage; baseUsages[item] = value; } item.batteryUsage = Mathf.Max(0.01f, value * mult); return value; } internal static void ReapplyAllCapacities() { if (!ConfigSync.IsActive || reapplying) { return; } reapplying = true; try { GrabbableObject[] array = Object.FindObjectsOfType(); foreach (GrabbableObject val in array) { Item val2 = (((Object)(object)val != (Object)null) ? val.itemProperties : null); if (!((Object)(object)val2 == (Object)null) && val2.requiresBattery && !(val2.batteryUsage <= 0f)) { string usedKey; bool individual; float mult = ResolveMultiplier(val, val2, out usedKey, out individual); ApplyCapacity(val2, mult); } } } catch (Exception arg) { Plugin.Log.LogError((object)$"Error while re-applying capacities: {arg}"); } finally { reapplying = false; } } private static float ResolveMultiplier(GrabbableObject obj, Item item, out string usedKey, out bool individual) { usedKey = null; individual = false; Plugin.EnsureIndividualEntry(Plugin.SanitizeKey((!string.IsNullOrEmpty(item.itemName)) ? item.itemName : ((object)obj).GetType().Name)); if (Plugin.EnableIndividualMultipliers.Value) { foreach (string item2 in CandidateKeys(obj, item)) { ConfigEntry val = Plugin.FindIndividual(item2); if (val != null && val.Value >= 0f) { usedKey = ((ConfigEntryBase)val).Definition.Key; individual = true; return val.Value; } } } return Plugin.GlobalMultiplier.Value; } private static IEnumerable CandidateKeys(GrabbableObject obj, Item item) { if (!string.IsNullOrEmpty(item.itemName)) { yield return item.itemName; } if ((Object)(object)((Component)obj).gameObject != (Object)null) { string text = ((Object)((Component)obj).gameObject).name.Replace("(Clone)", "").Trim(); if (!string.IsNullOrEmpty(text)) { yield return text; } } yield return ((object)obj).GetType().Name; } private static string Describe(GrabbableObject obj, Item item) { return (string.IsNullOrEmpty(item.itemName) ? "?" : item.itemName) + " " + $"[type={((object)obj).GetType().Name}, scrap={item.isScrap}]"; } } [HarmonyPatch(typeof(ItemCharger), "ChargeItem")] internal static class ItemChargerPatch { internal static int chargesUsed; private static readonly Dictionary baseTips = new Dictionary(); [HarmonyPrefix] private static bool Prefix(ItemCharger __instance) { if (!ConfigSync.IsActive) { return true; } int value = Plugin.MaxCharges.Value; if (value <= 0) { return true; } PlayerControllerB val = (((Object)(object)GameNetworkManager.Instance != (Object)null) ? GameNetworkManager.Instance.localPlayerController : null); GrabbableObject val2 = (((Object)(object)val != (Object)null) ? val.currentlyHeldObjectServer : null); if ((Object)(object)val2 == (Object)null || (Object)(object)val2.itemProperties == (Object)null || !val2.itemProperties.requiresBattery) { return true; } if (chargesUsed >= value) { if ((Object)(object)HUDManager.Instance != (Object)null) { HUDManager.Instance.DisplayTip(Loc.ChargerTipHeader, Loc.NoChargesLeft, true, false, "LC_Tip1"); } RefreshTip(__instance); return false; } chargesUsed++; if (Plugin.VerboseLogging.Value) { Plugin.Log.LogInfo((object)$"[Charger] used {chargesUsed}/{value}"); } RefreshTip(__instance); return true; } internal static void RefreshTip(ItemCharger charger) { if ((Object)(object)charger == (Object)null) { return; } InteractTrigger triggerScript = charger.triggerScript; if (!((Object)(object)triggerScript == (Object)null)) { if (!baseTips.TryGetValue(triggerScript, out var value)) { value = triggerScript.hoverTip; baseTips[triggerScript] = value; } if (ConfigSync.IsActive && Plugin.MaxCharges.Value > 0) { int num = Mathf.Max(0, Plugin.MaxCharges.Value - chargesUsed); triggerScript.hoverTip = $"{value} [{num}]"; } else { triggerScript.hoverTip = value; } } } internal static void RefreshAllTips() { ItemCharger[] array = Object.FindObjectsOfType(); for (int i = 0; i < array.Length; i++) { RefreshTip(array[i]); } } } [HarmonyPatch(typeof(StartOfRound), "StartGame")] internal static class ChargerDailyResetPatch { [HarmonyPostfix] private static void Postfix() { if (Plugin.ResetChargesEachDay.Value) { ItemChargerPatch.chargesUsed = 0; if (Plugin.VerboseLogging.Value) { Plugin.Log.LogInfo((object)"[Charger] daily charges reset."); } } ItemChargerPatch.RefreshAllTips(); } } internal static class ModInfo { public const string Guid = "ExhaustedBatteries"; public const string Name = "Exhausted Batteries"; public const string Version = "1.0.0"; } [BepInPlugin("ExhaustedBatteries", "Exhausted Batteries", "1.0.0")] public class Plugin : BaseUnityPlugin { internal static ManualLogSource Log; public const string GeneralSection = "General"; public const string ChargerSection = "ChargingStation"; public const string FlashlightSection = "Flashlight"; public const string LocalizationSection = "Localization"; public const string IndividualSection = "IndividualMultipliers"; public const string ModdedSection = "ModdedItems"; public static ConfigEntry GlobalMultiplier; public static ConfigEntry EnableIndividualMultipliers; public static ConfigEntry SpawnStoreItemsDischarged; public static ConfigEntry InitialChargeMultiplier; public static ConfigEntry VerboseLogging; public static ConfigEntry MaxCharges; public static ConfigEntry ResetChargesEachDay; public static ConfigEntry EnableFlashlightDimming; public static ConfigEntry DimStartCharge; public static ConfigEntry MinBrightness; internal static ConfigEntry LanguageSetting; private static readonly Dictionary> normalizedIndex = new Dictionary>(StringComparer.Ordinal); private Harmony harmony; public static Plugin Instance { get; private set; } internal static ConfigFile Cfg => ((BaseUnityPlugin)Instance).Config; internal static IEnumerable> ItemEntries => normalizedIndex.Values; private void Awake() { //IL_003b: Unknown result type (might be due to invalid IL or missing references) //IL_0045: Expected O, but got Unknown Instance = this; Log = ((BaseUnityPlugin)this).Logger; BindGeneralConfig(); BindChargerConfig(); BindFlashlightConfig(); BindLocalizationConfig(); BindModdedItems(); ImportExistingIndividualKeys(); harmony = new Harmony("ExhaustedBatteries"); harmony.PatchAll(); ((BaseUnityPlugin)this).Config.SettingChanged += delegate { if (!ConfigSync.Applying) { ConfigSync.EnforceHostAuthority(); ReapplyAll(); } }; Log.LogInfo((object)("Exhausted Batteries v1.0.0 loaded. " + $"Global={GlobalMultiplier.Value}, Individual={EnableIndividualMultipliers.Value}, " + $"DischargeStore={SpawnStoreItemsDischarged.Value}, MaxCharges={MaxCharges.Value}")); } private void BindGeneralConfig() { //IL_002e: Unknown result type (might be due to invalid IL or missing references) //IL_0038: Expected O, but got Unknown //IL_00ab: Unknown result type (might be due to invalid IL or missing references) //IL_00b5: Expected O, but got Unknown GlobalMultiplier = ((BaseUnityPlugin)this).Config.Bind("General", "GlobalMultiplier", 1f, new ConfigDescription("Global battery-capacity multiplier applied to ALL items that use a battery. 1.0 = vanilla, 2.0 = lasts twice as long, 0.5 = half as long.", (AcceptableValueBase)(object)new AcceptableValueRange(0.001f, 1000f), Array.Empty())); EnableIndividualMultipliers = ((BaseUnityPlugin)this).Config.Bind("General", "EnableIndividualMultipliers", false, "Enable per-item multipliers from [IndividualMultipliers] and [ModdedItems]. A per-item multiplier takes priority over the global one."); SpawnStoreItemsDischarged = ((BaseUnityPlugin)this).Config.Bind("General", "SpawnStoreItemsDischarged", true, "Items with a battery bought from the store arrive fully discharged (charge = 0). Does not affect scrap/loot found on the map."); InitialChargeMultiplier = ((BaseUnityPlugin)this).Config.Bind("General", "InitialChargeMultiplier", 1f, new ConfigDescription("Multiplier for the INITIAL charge of items that are NOT force-discharged (usually loot/scrap). 1.0 = vanilla. Result is clamped to 0..1.", (AcceptableValueBase)(object)new AcceptableValueRange(0f, 1f), Array.Empty())); VerboseLogging = ((BaseUnityPlugin)this).Config.Bind("General", "VerboseLogging", false, "Verbose logging of applied multipliers to the BepInEx console (for debugging)."); } private void BindChargerConfig() { //IL_0026: Unknown result type (might be due to invalid IL or missing references) //IL_0030: Expected O, but got Unknown MaxCharges = ((BaseUnityPlugin)this).Config.Bind("ChargingStation", "MaxCharges", 0, new ConfigDescription("Maximum number of times the ship's charging station can be used. 0 = unlimited (vanilla).", (AcceptableValueBase)(object)new AcceptableValueRange(0, 999), Array.Empty())); ResetChargesEachDay = ((BaseUnityPlugin)this).Config.Bind("ChargingStation", "ResetChargesEachDay", true, "If true, the charging-station use counter resets every day (when the ship lands). If false, MaxCharges is a total limit for the whole session."); } private void BindFlashlightConfig() { //IL_004e: Unknown result type (might be due to invalid IL or missing references) //IL_0058: Expected O, but got Unknown //IL_008b: Unknown result type (might be due to invalid IL or missing references) //IL_0095: Expected O, but got Unknown EnableFlashlightDimming = ((BaseUnityPlugin)this).Config.Bind("Flashlight", "EnableFlashlightDimming", false, "Flashlights get dimmer as their battery drains (separate optional feature, off by default)."); DimStartCharge = ((BaseUnityPlugin)this).Config.Bind("Flashlight", "DimStartCharge", 1f, new ConfigDescription("Charge fraction (0..1) at/above which the flashlight stays at full brightness. Below it, brightness fades toward MinBrightness as the battery drops to empty. 1.0 = dim across the whole charge range.", (AcceptableValueBase)(object)new AcceptableValueRange(0f, 1f), Array.Empty())); MinBrightness = ((BaseUnityPlugin)this).Config.Bind("Flashlight", "MinBrightness", 0.15f, new ConfigDescription("Brightness fraction (0..1) at empty battery. 0 = almost dark, 1 = no dimming.", (AcceptableValueBase)(object)new AcceptableValueRange(0f, 1f), Array.Empty())); } private void BindLocalizationConfig() { LanguageSetting = ((BaseUnityPlugin)this).Config.Bind("Localization", "Language", Loc.Language.Auto, "Language for this mod's in-game messages. Auto = Russian if a Russian localization mod (e.g. RTLC) is loaded or the system language is Russian, otherwise English. The config file itself stays in English."); } private void BindModdedItems() { RegisterItemEntry("ModdedItems", "Mapper", "Per-item capacity multiplier for the Mapper (mod: restoremapper). -1 = use GlobalMultiplier."); RegisterItemEntry("ModdedItems", "Geigercounter", "Per-item capacity multiplier for the Geiger counter (mod: facilitymeltdown). -1 = use GlobalMultiplier."); } private ConfigEntry RegisterItemEntry(string section, string key, string description) { //IL_0022: Unknown result type (might be due to invalid IL or missing references) //IL_002c: Expected O, but got Unknown ConfigEntry val = ((BaseUnityPlugin)this).Config.Bind(section, key, -1f, new ConfigDescription(description, (AcceptableValueBase)(object)new AcceptableValueRange(-1f, 1000f), Array.Empty())); normalizedIndex[Normalize(key)] = val; return val; } public static void EnsureIndividualEntry(string canonicalKey) { if (!string.IsNullOrEmpty(canonicalKey) && !normalizedIndex.ContainsKey(Normalize(canonicalKey))) { Instance.RegisterItemEntry("IndividualMultipliers", canonicalKey, "Per-item capacity multiplier for '" + canonicalKey + "'. -1 = use GlobalMultiplier. Requires EnableIndividualMultipliers = true."); } } internal static void ReapplyAll() { Loc.Invalidate(); GrabbableObjectPatch.ReapplyAllCapacities(); ItemChargerPatch.RefreshAllTips(); FlashlightDimPatch.OnSettingsChanged(); } public static ConfigEntry FindIndividual(string candidate) { if (string.IsNullOrEmpty(candidate)) { return null; } if (!normalizedIndex.TryGetValue(Normalize(candidate), out var value)) { return null; } return value; } public static string Normalize(string s) { if (string.IsNullOrEmpty(s)) { return string.Empty; } return new string(s.Where(char.IsLetterOrDigit).ToArray()).ToLowerInvariant(); } public static string SanitizeKey(string s) { if (string.IsNullOrWhiteSpace(s)) { return "Unknown"; } StringBuilder stringBuilder = new StringBuilder(s.Length); string text = s.Trim(); foreach (char c in text) { stringBuilder.Append(("=\n\t\\\"'[]".IndexOf(c) >= 0) ? '_' : c); } string text2 = stringBuilder.ToString().Trim(); if (text2.Length != 0) { return text2; } return "Unknown"; } private void ImportExistingIndividualKeys() { try { if (!(typeof(ConfigFile).GetProperty("OrphanedEntries", BindingFlags.Instance | BindingFlags.NonPublic)?.GetValue(((BaseUnityPlugin)this).Config) is IDictionary dictionary)) { return; } foreach (ConfigDefinition item in dictionary.Keys.ToList()) { if (string.Equals(item.Section, "IndividualMultipliers", StringComparison.Ordinal)) { EnsureIndividualEntry(item.Key); } } } catch (Exception ex) { Log.LogWarning((object)("Could not import existing [IndividualMultipliers] keys: " + ex.Message + ". Prefer the keys the mod generates automatically.")); } } } }