Please disclose if any significant portion of your mod was created using AI tools by adding the 'AI Generated' category. Failing to do so may result in the mod being removed from Thunderstore.
Decompiled source of ProgressivePowers v0.1.0
plugins/ProgressivePowers.dll
Decompiled 15 hours agousing System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Runtime.Versioning; using System.Security.Permissions; using System.Text; using System.Text.RegularExpressions; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using HarmonyLib; using Jotunn.Entities; using Jotunn.Managers; using Jotunn.Utils; using ProgressivePowers.Common; using ProgressivePowers.Modules; using SimpleJson; using UnityEngine; using UnityEngine.UI; using YamlDotNet.Serialization; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: AssemblyTitle("ProgressivePowers")] [assembly: AssemblyDescription("Turns the Forsaken powers into passives that deepen as you kill bosses.")] [assembly: AssemblyConfiguration("")] [assembly: AssemblyCompany("")] [assembly: AssemblyProduct("ProgressivePowers")] [assembly: AssemblyCopyright("Copyright © 2026")] [assembly: AssemblyTrademark("")] [assembly: ComVisible(false)] [assembly: Guid("6b1f0c74-9d2e-4a55-8f31-2c7a4e5b9d10")] [assembly: AssemblyFileVersion("0.1.0")] [assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("0.1.0.0")] namespace ProgressivePowers { internal class Logger { public static LogLevel Level = (LogLevel)16; public static void EnableDebugLogging(object sender, EventArgs e) { CheckEnableDebugLogging(); } public static void CheckEnableDebugLogging() { //IL_0016: Unknown result type (might be due to invalid IL or missing references) //IL_000e: Unknown result type (might be due to invalid IL or missing references) if (ValConfig.EnableDebugMode.Value) { Level = (LogLevel)32; } else { Level = (LogLevel)16; } } public static void SetDebugLogging(bool state) { //IL_000d: Unknown result type (might be due to invalid IL or missing references) //IL_0005: Unknown result type (might be due to invalid IL or missing references) if (state) { Level = (LogLevel)32; } else { Level = (LogLevel)16; } } public static void LogDebug(string message) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0007: Invalid comparison between Unknown and I4 if ((int)Level >= 32) { ProgressivePowers.Log.LogInfo((object)("[DEBUG]" + message)); } } public static void LogInfo(string message) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0007: Invalid comparison between Unknown and I4 if ((int)Level >= 16) { ProgressivePowers.Log.LogInfo((object)message); } } public static void LogWarning(string message) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Invalid comparison between Unknown and I4 if ((int)Level >= 4) { ProgressivePowers.Log.LogWarning((object)message); } } public static void LogError(string message) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Invalid comparison between Unknown and I4 if ((int)Level >= 2) { ProgressivePowers.Log.LogError((object)message); } } } internal class ValConfig { public static ConfigFile cfg; public static ConfigEntry<bool> EnableDebugMode; public static ConfigEntry<int> DebugLevelOverride; public static ConfigEntry<float> ConfigApplyDelay; public static ConfigEntry<float> KillCreditRange; public static ConfigEntry<bool> ShowLockedFeatures; public static ConfigEntry<bool> ShowPassiveInStatusBar; public static ConfigEntry<bool> BlockPowerActivation; public static ConfigEntry<string> LevelRequirementsYaml; public const string cfgFolder = "ProgressivePowers"; public static int Generation { get; private set; } public static void MarkChanged() { Generation++; } public ValConfig(ConfigFile cf) { cfg = cf; cfg.SaveOnConfigSet = false; CreateConfigValues(cf); Logger.SetDebugLogging(EnableDebugMode.Value); } public static void SaveOnSet(bool enabled) { cfg.SaveOnConfigSet = enabled; cfg.Save(); } private void CreateConfigValues(ConfigFile Config) { //IL_001a: Unknown result type (might be due to invalid IL or missing references) //IL_001f: Unknown result type (might be due to invalid IL or missing references) //IL_002c: Expected O, but got Unknown //IL_002c: Unknown result type (might be due to invalid IL or missing references) //IL_0036: Expected O, but got Unknown //IL_0076: Unknown result type (might be due to invalid IL or missing references) //IL_007b: Unknown result type (might be due to invalid IL or missing references) //IL_0088: Expected O, but got Unknown //IL_0088: Unknown result type (might be due to invalid IL or missing references) //IL_0092: Expected O, but got Unknown //IL_015f: Unknown result type (might be due to invalid IL or missing references) //IL_0164: Unknown result type (might be due to invalid IL or missing references) //IL_016b: Unknown result type (might be due to invalid IL or missing references) //IL_0178: Expected O, but got Unknown //IL_0178: Unknown result type (might be due to invalid IL or missing references) //IL_0182: Expected O, but got Unknown EnableDebugMode = Config.Bind<bool>("Client config", "EnableDebugMode", false, new ConfigDescription("Enables Debug logging.", (AcceptableValueBase)null, new object[1] { (object)new ConfigurationManagerAttributes { IsAdvanced = true } })); EnableDebugMode.SettingChanged += Logger.EnableDebugLogging; Logger.CheckEnableDebugLogging(); DebugLevelOverride = Config.Bind<int>("Client config", "Debug Level Override", -1, new ConfigDescription("Forces every power to this mastery level regardless of kills, for testing. -1 disables the override. Treated as a cheat: only honored after 'devcommands' is enabled in the console, on a world you host.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(-1, 7), new object[1] { (object)new ConfigurationManagerAttributes { IsAdvanced = true } })); ConfigApplyDelay = BindServerConfig("General", "Config Apply Delay", 1f, "Delay in seconds before a changed config entry is applied in-game. Coalesces a burst of rapid edits (typing, file reloads, server sync) into a single apply. Set to 0 to apply instantly.", advanced: true, 0f, 10f); KillCreditRange = BindServerConfig("General", "Kill Credit Range", 200f, "How close you must be when a boss dies to be credited with the kill, in meters. Set to 0 for unlimited range.", advanced: false, 0f, 10000f); ShowLockedFeatures = BindServerConfig("General", "Show Locked Features", value: true, "Show not-yet-unlocked features greyed out, along with what you still need to kill."); ShowPassiveInStatusBar = BindServerConfig("General", "Show Passive In Status Bar", value: true, "Show the active passive as a permanent icon in the status effect bar."); BlockPowerActivation = BindServerConfig("General", "Block Power Activation", value: true, "Prevent Forsaken powers from being activated. Turning this off restores vanilla activation while keeping the passives."); LevelRequirementsYaml = cfg.Bind<string>("General", "Level Requirements", "", new ConfigDescription("Serialized contents of ProgressivePowers.Levels.yaml. Edit the file, not this entry - it exists so the server can push requirements to clients.", (AcceptableValueBase)null, new object[1] { (object)new ConfigurationManagerAttributes { IsAdminOnly = true, Browsable = false } })); PowerDefinitions.BindConfigs(); WireChangeNotifications(); } private static void WireChangeNotifications() { ShowLockedFeatures.SettingChanged += OnChanged; ShowPassiveInStatusBar.SettingChanged += OnChanged; BlockPowerActivation.SettingChanged += OnChanged; DebugLevelOverride.SettingChanged += OnChanged; BossPower[] all = PowerDefinitions.All; for (int i = 0; i < all.Length; i++) { PowerFeature[] features = all[i].Features; foreach (PowerFeature powerFeature in features) { if (!(powerFeature is FloatFeature floatFeature)) { if (!(powerFeature is DamageModFeature damageModFeature)) { if (powerFeature is AttributeFeature attributeFeature) { attributeFeature.Cfg.SettingChanged += OnChanged; } } else { damageModFeature.Cfg.SettingChanged += OnChanged; } } else { floatFeature.Cfg.SettingChanged += OnChanged; } } } static void OnChanged(object sender, EventArgs e) { MarkChanged(); ConfigChangeDebouncer.Schedule("ValConfig", delegate { PassivePowerManager.Refresh(force: true); }); } } public static ConfigEntry<float[]> BindServerConfig(string category, string key, float[] value, string description, bool advanced = false, float valMin = 0f, float valMax = 150f) { //IL_001a: Unknown result type (might be due to invalid IL or missing references) //IL_001f: Unknown result type (might be due to invalid IL or missing references) //IL_0026: Unknown result type (might be due to invalid IL or missing references) //IL_0034: Expected O, but got Unknown //IL_0034: Unknown result type (might be due to invalid IL or missing references) //IL_003e: Expected O, but got Unknown return cfg.Bind<float[]>(category, key, value, new ConfigDescription(description, (AcceptableValueBase)(object)new AcceptableValueRange<float>(valMin, valMax), new object[1] { (object)new ConfigurationManagerAttributes { IsAdminOnly = true, IsAdvanced = advanced } })); } public static ConfigEntry<bool> BindServerConfig(string category, string key, bool value, string description, AcceptableValueBase acceptableValues = null, bool advanced = false) { //IL_0013: Unknown result type (might be due to invalid IL or missing references) //IL_0018: Unknown result type (might be due to invalid IL or missing references) //IL_001f: Unknown result type (might be due to invalid IL or missing references) //IL_002d: Expected O, but got Unknown //IL_002d: Unknown result type (might be due to invalid IL or missing references) //IL_0037: Expected O, but got Unknown return cfg.Bind<bool>(category, key, value, new ConfigDescription(description, acceptableValues, new object[1] { (object)new ConfigurationManagerAttributes { IsAdminOnly = true, IsAdvanced = advanced } })); } public static ConfigEntry<int> BindServerConfig(string category, string key, int value, string description, bool advanced = false, int valMin = 0, int valMax = 150) { //IL_001a: Unknown result type (might be due to invalid IL or missing references) //IL_001f: Unknown result type (might be due to invalid IL or missing references) //IL_0026: Unknown result type (might be due to invalid IL or missing references) //IL_0034: Expected O, but got Unknown //IL_0034: Unknown result type (might be due to invalid IL or missing references) //IL_003e: Expected O, but got Unknown return cfg.Bind<int>(category, key, value, new ConfigDescription(description, (AcceptableValueBase)(object)new AcceptableValueRange<int>(valMin, valMax), new object[1] { (object)new ConfigurationManagerAttributes { IsAdminOnly = true, IsAdvanced = advanced } })); } public static ConfigEntry<float> BindServerConfig(string category, string key, float value, string description, bool advanced = false, float valMin = 0f, float valMax = 150f) { //IL_001a: Unknown result type (might be due to invalid IL or missing references) //IL_001f: Unknown result type (might be due to invalid IL or missing references) //IL_0026: Unknown result type (might be due to invalid IL or missing references) //IL_0034: Expected O, but got Unknown //IL_0034: Unknown result type (might be due to invalid IL or missing references) //IL_003e: Expected O, but got Unknown return cfg.Bind<float>(category, key, value, new ConfigDescription(description, (AcceptableValueBase)(object)new AcceptableValueRange<float>(valMin, valMax), new object[1] { (object)new ConfigurationManagerAttributes { IsAdminOnly = true, IsAdvanced = advanced } })); } public static ConfigEntry<string> BindServerConfig(string category, string key, string value, string description, AcceptableValueList<string> acceptableValues = null, bool advanced = false) { //IL_0013: Unknown result type (might be due to invalid IL or missing references) //IL_0018: Unknown result type (might be due to invalid IL or missing references) //IL_001f: Unknown result type (might be due to invalid IL or missing references) //IL_002d: Expected O, but got Unknown //IL_002d: Unknown result type (might be due to invalid IL or missing references) //IL_0037: Expected O, but got Unknown return cfg.Bind<string>(category, key, value, new ConfigDescription(description, (AcceptableValueBase)(object)acceptableValues, new object[1] { (object)new ConfigurationManagerAttributes { IsAdminOnly = true, IsAdvanced = advanced } })); } } internal static class KillTracker { internal const string CustomDataKey = "ProgressivePowers.Kills"; private static readonly Dictionary<string, int> counts = new Dictionary<string, int>(); private static Player boundPlayer; private static bool loaded; private static bool reportedFresh; internal static IEnumerable<KeyValuePair<string, int>> All => counts; internal static int Get(string yamlName) { if (!counts.TryGetValue(yamlName, out var value)) { return 0; } return value; } internal static void Bind(Player player) { if (!((Object)(object)player == (Object)null)) { if ((Object)(object)boundPlayer != (Object)(object)player) { reportedFresh = false; } boundPlayer = player; counts.Clear(); loaded = false; if (player.m_customData.TryGetValue("ProgressivePowers.Kills", out var value) && !string.IsNullOrEmpty(value)) { Parse(value); loaded = true; Logger.LogDebug("Loaded boss kills for " + player.GetPlayerName() + ": " + Serialize()); } else { Migrate(player); } } } internal static void EnsureLoaded(Player player) { if (!((Object)(object)player == (Object)null) && (!((Object)(object)boundPlayer == (Object)(object)player) || !loaded)) { Bind(player); } } private static void Migrate(Player player) { BossPower[] all = PowerDefinitions.All; foreach (BossPower bossPower in all) { if (((Humanoid)player).HaveUniqueKey(bossPower.BossKey)) { counts[bossPower.YamlName] = 1; } } if (counts.Count > 0) { loaded = true; Save(player); Logger.LogInfo("No kill record for " + player.GetPlayerName() + "; seeded from defeated_* keys: " + Serialize()); } else if (!reportedFresh) { reportedFresh = true; Logger.LogDebug("No kill record for " + player.GetPlayerName() + " and no defeated_* keys yet; retrying until they load."); } } internal static void RecordKill(Player player, BossPower power) { EnsureLoaded(player); counts.TryGetValue(power.YamlName, out var value); counts[power.YamlName] = value + 1; boundPlayer = player; loaded = true; Save(player); Logger.LogInfo($"Recorded {power.YamlName} kill #{value + 1}."); } internal static void Flush(Player player) { if (!((Object)(object)player == (Object)null) && !((Object)(object)boundPlayer != (Object)(object)player) && loaded) { Save(player); } } private static void Save(Player player) { if (!((Object)(object)player == (Object)null)) { player.m_customData["ProgressivePowers.Kills"] = Serialize(); } } private static string Serialize() { StringBuilder stringBuilder = new StringBuilder(); foreach (KeyValuePair<string, int> count in counts) { if (count.Value > 0) { if (stringBuilder.Length > 0) { stringBuilder.Append(','); } stringBuilder.Append(count.Key).Append(':').Append(count.Value); } } return stringBuilder.ToString(); } private static void Parse(string raw) { string[] array = raw.Split(new char[1] { ',' }); for (int i = 0; i < array.Length; i++) { string[] array2 = array[i].Split(new char[1] { ':' }); if (array2.Length == 2) { string text = array2[0].Trim(); int result; if (PowerDefinitions.ByYamlName(text) == null) { Logger.LogWarning("Ignoring unknown boss '" + text + "' in stored kill counts."); } else if (int.TryParse(array2[1].Trim(), out result) && result > 0) { counts[text] = result; } } } } } internal static class LevelRequirements { private const string FileName = "ProgressivePowers.Levels.yaml"; private static Dictionary<string, Dictionary<int, Dictionary<string, int>>> table = new Dictionary<string, Dictionary<int, Dictionary<string, int>>>(); private static FileSystemWatcher watcher; private static bool warnedOverrideNeedsCheats; internal static string FilePath => Path.Combine(Paths.ConfigPath, "ProgressivePowers", "ProgressivePowers.Levels.yaml"); internal static void Initialize() { try { EnsureFileExists(); string text = File.ReadAllText(FilePath); if (ValConfig.LevelRequirementsYaml.Value != text) { ValConfig.LevelRequirementsYaml.Value = text; } ReloadFromConfig(); StartWatching(); } catch (Exception ex) { Logger.LogError("Could not initialize level requirements, falling back to defaults: " + ex.Message); table = BuildDefaultTable(); } ValConfig.DebugLevelOverride.SettingChanged += delegate { warnedOverrideNeedsCheats = false; }; ValConfig.LevelRequirementsYaml.SettingChanged += delegate { ConfigChangeDebouncer.Schedule("LevelRequirements", delegate { ReloadFromConfig(); ValConfig.MarkChanged(); PassivePowerManager.Refresh(force: true); }); }; } private static void EnsureFileExists() { string directoryName = Path.GetDirectoryName(FilePath); if (!Directory.Exists(directoryName)) { Directory.CreateDirectory(directoryName); } if (!File.Exists(FilePath)) { File.WriteAllText(FilePath, BuildDefaultYaml()); Logger.LogInfo("Wrote default level requirements to " + FilePath); } } private static void StartWatching() { watcher = new FileSystemWatcher(Path.GetDirectoryName(FilePath), "ProgressivePowers.Levels.yaml") { NotifyFilter = (NotifyFilters.FileName | NotifyFilters.LastWrite | NotifyFilters.CreationTime), EnableRaisingEvents = true }; watcher.Changed += OnFileChanged; watcher.Created += OnFileChanged; watcher.Renamed += OnFileChanged; } internal static void Shutdown() { watcher?.Dispose(); watcher = null; } private static void OnFileChanged(object sender, FileSystemEventArgs e) { ThreadingHelper.Instance.StartSyncInvoke((Action)delegate { if ((Object)(object)ZNet.instance != (Object)null && !ZNet.instance.IsServer()) { Logger.LogWarning("ProgressivePowers.Levels.yaml changed, but this is a client - the server's level requirements stay in force."); } else { ConfigChangeDebouncer.Schedule("OnFileChanged", delegate { try { ValConfig.LevelRequirementsYaml.Value = File.ReadAllText(FilePath); } catch (Exception ex) { Logger.LogError("Could not re-read ProgressivePowers.Levels.yaml: " + ex.Message); } }); } }); } private static void ReloadFromConfig() { string value = ValConfig.LevelRequirementsYaml.Value; if (string.IsNullOrWhiteSpace(value)) { table = BuildDefaultTable(); return; } Dictionary<string, Dictionary<int, Dictionary<string, int>>> dictionary = Parse(value); if (dictionary != null) { table = dictionary; } } private static Dictionary<string, Dictionary<int, Dictionary<string, int>>> Parse(string text) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) Dictionary<string, Dictionary<string, Dictionary<string, string>>> dictionary; try { dictionary = new DeserializerBuilder().Build().Deserialize<Dictionary<string, Dictionary<string, Dictionary<string, string>>>>(text); } catch (Exception ex) { Logger.LogError("ProgressivePowers.Levels.yaml is not valid YAML, keeping the previous requirements: " + ex.Message); return null; } if (dictionary == null) { Logger.LogWarning("ProgressivePowers.Levels.yaml is empty, using defaults."); return BuildDefaultTable(); } Dictionary<string, Dictionary<int, Dictionary<string, int>>> dictionary2 = new Dictionary<string, Dictionary<int, Dictionary<string, int>>>(); foreach (KeyValuePair<string, Dictionary<string, Dictionary<string, string>>> item in dictionary) { if (PowerDefinitions.ByYamlName(item.Key) == null) { Logger.LogWarning("ProgressivePowers.Levels.yaml: unknown power '" + item.Key + "', ignored."); continue; } Dictionary<int, Dictionary<string, int>> dictionary3 = new Dictionary<int, Dictionary<string, int>>(); foreach (KeyValuePair<string, Dictionary<string, string>> item2 in item.Value ?? new Dictionary<string, Dictionary<string, string>>()) { if (!int.TryParse(item2.Key, out var result) || result < 1 || result > 7) { Logger.LogWarning(string.Format("{0}: {1} has level '{2}', expected 1-{3}. Ignored.", "ProgressivePowers.Levels.yaml", item.Key, item2.Key, 7)); continue; } Dictionary<string, int> dictionary4 = new Dictionary<string, int>(); foreach (KeyValuePair<string, string> item3 in item2.Value ?? new Dictionary<string, string>()) { int result2; if (PowerDefinitions.ByYamlName(item3.Key) == null) { Logger.LogWarning(string.Format("{0}: {1} level {2} requires unknown boss '{3}', ignored.", "ProgressivePowers.Levels.yaml", item.Key, result, item3.Key)); } else if (!int.TryParse(item3.Value, out result2) || result2 < 0) { Logger.LogWarning(string.Format("{0}: {1} level {2} has a bad kill count for '{3}', ignored.", "ProgressivePowers.Levels.yaml", item.Key, result, item3.Key)); } else if (result2 > 0) { dictionary4[item3.Key] = result2; } } dictionary3[result] = dictionary4; } if (dictionary3.Count < 7) { List<string> list = new List<string>(); for (int i = 1; i <= 7; i++) { if (!dictionary3.ContainsKey(i)) { list.Add(i.ToString()); } } Logger.LogWarning("ProgressivePowers.Levels.yaml: " + item.Key + " does not define level(s) " + string.Join(", ", list) + "; they will unlock for free once the levels below them are met."); } dictionary2[item.Key] = dictionary3; } Dictionary<string, Dictionary<int, Dictionary<string, int>>> dictionary5 = null; BossPower[] all = PowerDefinitions.All; foreach (BossPower bossPower in all) { if (!dictionary2.ContainsKey(bossPower.YamlName)) { if (dictionary5 == null) { dictionary5 = BuildDefaultTable(); } dictionary2[bossPower.YamlName] = dictionary5[bossPower.YamlName]; Logger.LogWarning("ProgressivePowers.Levels.yaml: no entry for '" + bossPower.YamlName + "', using defaults for it."); } } return dictionary2; } internal static Dictionary<string, int> CumulativeRequirement(BossPower power, int level) { Dictionary<string, int> dictionary = new Dictionary<string, int>(); if (!table.TryGetValue(power.YamlName, out var value)) { return dictionary; } for (int i = 1; i <= level; i++) { if (!value.TryGetValue(i, out var value2)) { continue; } foreach (KeyValuePair<string, int> item in value2) { if (!dictionary.TryGetValue(item.Key, out var value3) || item.Value > value3) { dictionary[item.Key] = item.Value; } } } return dictionary; } internal static Dictionary<string, int> Outstanding(BossPower power, int level) { Dictionary<string, int> dictionary = new Dictionary<string, int>(); foreach (KeyValuePair<string, int> item in CumulativeRequirement(power, level)) { int num = item.Value - KillTracker.Get(item.Key); if (num > 0) { dictionary[item.Key] = num; } } return dictionary; } internal static int GetLevel(BossPower power) { int value = ValConfig.DebugLevelOverride.Value; if (value >= 0) { if ((Object)(object)Console.instance != (Object)null && ((Terminal)Console.instance).IsCheatsEnabled()) { return Math.Min(value, 7); } if (!warnedOverrideNeedsCheats) { warnedOverrideNeedsCheats = true; Logger.LogWarning("Debug Level Override is set but ignored: it requires 'devcommands' in the console, on a world you host."); } } int result = 0; for (int i = 1; i <= 7 && Outstanding(power, i).Count <= 0; i++) { result = i; } return result; } private static List<KeyValuePair<string, int>> DefaultLevels(int powerIndex) { BossPower[] all = PowerDefinitions.All; string yamlName = all[powerIndex].YamlName; string yamlName2 = all[^1].YamlName; Dictionary<string, int> dictionary = new Dictionary<string, int>(); List<KeyValuePair<string, int>> list = new List<KeyValuePair<string, int>>(); int num = powerIndex; bool flag = true; for (int i = 1; i <= 7; i++) { string key; int value; if (num < all.Length) { key = all[num].YamlName; value = 1; num++; } else { key = ((yamlName == yamlName2) ? yamlName : (flag ? yamlName : yamlName2)); flag = !flag; dictionary.TryGetValue(key, out var value2); value = value2 + 1; } dictionary[key] = value; list.Add(new KeyValuePair<string, int>(key, value)); } return list; } private static Dictionary<string, Dictionary<int, Dictionary<string, int>>> BuildDefaultTable() { Dictionary<string, Dictionary<int, Dictionary<string, int>>> dictionary = new Dictionary<string, Dictionary<int, Dictionary<string, int>>>(); for (int i = 0; i < PowerDefinitions.All.Length; i++) { Dictionary<int, Dictionary<string, int>> dictionary2 = new Dictionary<int, Dictionary<string, int>>(); List<KeyValuePair<string, int>> list = DefaultLevels(i); for (int j = 0; j < list.Count; j++) { dictionary2[j + 1] = new Dictionary<string, int> { { list[j].Key, list[j].Value } }; } dictionary[PowerDefinitions.All[i].YamlName] = dictionary2; } return dictionary; } private static string BuildDefaultYaml() { StringBuilder stringBuilder = new StringBuilder(); stringBuilder.AppendLine("# Progressive Powers - mastery level requirements."); stringBuilder.AppendLine("#"); stringBuilder.AppendLine("# Each power has 7 levels. A level lists boss kill counts; ALL of them must be met,"); stringBuilder.AppendLine("# and levels are cumulative - reaching level 3 also requires levels 1 and 2. That is"); stringBuilder.AppendLine("# why each level below only names what it adds."); stringBuilder.AppendLine("#"); stringBuilder.AppendLine("# Counts are repeat kills by your own character, so you can mix them freely:"); stringBuilder.AppendLine("# 4: { Eikthyr: 3, TheElder: 2 }"); stringBuilder.AppendLine("# means level 4 needs three Eikthyr kills and two Elder kills."); stringBuilder.AppendLine("#"); stringBuilder.AppendLine("# Valid boss names: " + string.Join(", ", Array.ConvertAll(PowerDefinitions.All, (BossPower p) => p.YamlName))); stringBuilder.AppendLine(); for (int num = 0; num < PowerDefinitions.All.Length; num++) { stringBuilder.AppendLine(PowerDefinitions.All[num].YamlName + ":"); List<KeyValuePair<string, int>> list = DefaultLevels(num); for (int num2 = 0; num2 < list.Count; num2++) { stringBuilder.AppendLine($" {num2 + 1}: {{ {list[num2].Key}: {list[num2].Value} }}"); } stringBuilder.AppendLine(); } return stringBuilder.ToString(); } } internal static class PassivePowerManager { private static bool built; private static string lastSignature; internal static BossPower CurrentPower { get; private set; } internal static int CurrentLevel { get; private set; } internal static void BuildPassiveEffects() { if (built) { return; } if ((Object)(object)ObjectDB.instance == (Object)null || (Object)(object)ObjectDB.instance.GetStatusEffect(StringExtensionMethods.GetStableHashCode("GP_Eikthyr")) == (Object)null) { Logger.LogDebug("ObjectDB is not ready; passives will be built on a later attempt."); return; } BossPower[] all = PowerDefinitions.All; foreach (BossPower bossPower in all) { bossPower.PowerHash = StringExtensionMethods.GetStableHashCode(bossPower.PowerName); bossPower.PassiveHash = StringExtensionMethods.GetStableHashCode(bossPower.PassiveName); StatusEffect statusEffect = ObjectDB.instance.GetStatusEffect(bossPower.PowerHash); if ((Object)(object)statusEffect == (Object)null) { Logger.LogWarning("Vanilla status effect '" + bossPower.PowerName + "' was not found in ObjectDB; '" + bossPower.PassiveName + "' will have no icon."); } SE_Stats val = ScriptableObject.CreateInstance<SE_Stats>(); ((Object)val).name = bossPower.PassiveName; ((Object)val).hideFlags = (HideFlags)61; ((StatusEffect)val).m_name = (((Object)(object)statusEffect != (Object)null) ? statusEffect.m_name : bossPower.LocKey); ((StatusEffect)val).m_icon = (((Object)(object)statusEffect != (Object)null) ? statusEffect.m_icon : null); ((StatusEffect)val).m_category = ""; ((StatusEffect)val).m_startMessage = ""; ((StatusEffect)val).m_stopMessage = ""; ((StatusEffect)val).m_repeatMessage = ""; ((StatusEffect)val).m_repeatInterval = 0f; bossPower.PassiveSE = val; Neutralize(val); } built = true; Logger.LogInfo($"Built {PowerDefinitions.All.Length} passive power effects."); Refresh(force: true); } internal static void Refresh(bool force = false) { if (!built) { BuildPassiveEffects(); } Player localPlayer = Player.m_localPlayer; if ((Object)(object)localPlayer == (Object)null || !built) { lastSignature = null; return; } KillTracker.EnsureLoaded(localPlayer); BossPower bossPower = PowerDefinitions.ByPowerName(localPlayer.GetGuardianPowerName()); int num = ((bossPower != null) ? LevelRequirements.GetLevel(bossPower) : 0); CurrentPower = bossPower; CurrentLevel = num; SEMan sEMan = ((Character)localPlayer).GetSEMan(); string text = $"{bossPower?.PassiveName}|{num}|{ValConfig.Generation}"; if (!force && text == lastSignature && StateMatches(sEMan, bossPower, num)) { return; } lastSignature = text; BossPower[] all = PowerDefinitions.All; foreach (BossPower bossPower2 in all) { if (bossPower2.PassiveHash != 0) { sEMan.RemoveStatusEffect(bossPower2.PassiveHash, true); } } if (bossPower == null || num <= 0) { Logger.LogDebug((bossPower == null) ? "No power selected." : (bossPower.YamlName + " is at level 0; no passive applied.")); return; } BuildPassive(bossPower, num); sEMan.AddStatusEffect((StatusEffect)(object)bossPower.PassiveSE, true, 0, 0f, (short)(-1)); Logger.LogDebug($"Applied {bossPower.PassiveName} at level {num}/{7}."); } private static bool StateMatches(SEMan seman, BossPower want, int level) { BossPower[] all = PowerDefinitions.All; foreach (BossPower bossPower in all) { bool flag = bossPower == want && level > 0; if (seman.HaveStatusEffect(bossPower.PassiveHash) != flag) { return false; } } return true; } internal static void BuildPassive(BossPower power, int level) { SE_Stats passiveSE = power.PassiveSE; Neutralize(passiveSE); PassiveBuild build = new PassiveBuild(passiveSE); for (int i = 0; i < level && i < power.Features.Length; i++) { PowerFeature powerFeature = power.Features[i]; if (powerFeature.Enabled) { powerFeature.Apply(build); } } if (!ValConfig.ShowPassiveInStatusBar.Value) { ((StatusEffect)passiveSE).m_icon = null; } else { RestoreIcon(power); } } private static void RestoreIcon(BossPower power) { if (!((Object)(object)((StatusEffect)power.PassiveSE).m_icon != (Object)null) && !((Object)(object)ObjectDB.instance == (Object)null)) { StatusEffect statusEffect = ObjectDB.instance.GetStatusEffect(power.PowerHash); if ((Object)(object)statusEffect != (Object)null) { ((StatusEffect)power.PassiveSE).m_icon = statusEffect.m_icon; } } } private static void Neutralize(SE_Stats se) { //IL_0090: Unknown result type (might be due to invalid IL or missing references) //IL_0095: Unknown result type (might be due to invalid IL or missing references) //IL_0141: Unknown result type (might be due to invalid IL or missing references) //IL_0153: Unknown result type (might be due to invalid IL or missing references) //IL_0165: Unknown result type (might be due to invalid IL or missing references) //IL_01a7: Unknown result type (might be due to invalid IL or missing references) //IL_01af: Unknown result type (might be due to invalid IL or missing references) //IL_01c1: Unknown result type (might be due to invalid IL or missing references) ((StatusEffect)se).m_ttl = 0f; ((StatusEffect)se).m_cooldown = 0f; se.m_runStaminaDrainModifier = 0f; se.m_jumpStaminaUseModifier = 0f; se.m_swimStaminaUseModifier = 0f; se.m_dodgeStaminaUseModifier = 0f; se.m_attackStaminaUseModifier = 0f; se.m_blockStaminaUseModifier = 0f; se.m_blockStaminaUseFlatValue = 0f; se.m_sneakStaminaUseModifier = 0f; se.m_homeItemStaminaUseModifier = 0f; se.m_speedModifier = 0f; se.m_swimSpeedModifier = 0f; se.m_jumpModifier = Vector3.zero; se.m_windMovementModifier = 0f; se.m_windRunStaminaModifier = 0f; se.m_fallDamageModifier = 0f; se.m_maxMaxFallSpeed = 0f; se.m_healthRegenMultiplier = 1f; se.m_staminaRegenMultiplier = 1f; se.m_eitrRegenMultiplier = 1f; se.m_addArmor = 0f; se.m_armorMultiplier = 0f; se.m_addMaxCarryWeight = 0f; se.m_noiseModifier = 0f; se.m_stealthModifier = 0f; se.m_adrenalineModifier = 0f; se.m_staggerModifier = 0f; se.m_timedBlockBonus = 0f; se.m_raiseSkill = (SkillType)0; se.m_raiseSkillModifier = 0f; se.m_skillLevel = (SkillType)0; se.m_skillLevelModifier = 0f; se.m_skillLevel2 = (SkillType)0; se.m_skillLevelModifier2 = 0f; se.m_tickInterval = 0f; se.m_healthPerTick = 0f; se.m_healthPerTickMinHealthPercentage = 0f; se.m_mods = new List<DamageModPair>(); se.m_percentigeDamageModifiers = default(DamageTypes); se.m_modifyAttackSkill = (SkillType)0; se.m_damageModifier = 1f; ((StatusEffect)se).m_attributes = (StatusAttribute)0; } } internal sealed class BossPower { internal string PowerName; internal string PassiveName; internal string BossKey; internal string YamlName; internal string LocKey; internal string ConfigCategory; internal PowerFeature[] Features; internal int PowerHash; internal int PassiveHash; internal SE_Stats PassiveSE; internal const int MaxLevel = 7; } internal static class PowerDefinitions { internal static readonly BossPower[] All = Build(); private static Dictionary<string, BossPower> byPowerName; private static Dictionary<string, BossPower> byYamlName; private const float PctMin = -1f; private const float PctMax = 5f; internal static BossPower ByPowerName(string name) { if (string.IsNullOrEmpty(name)) { return null; } if (byPowerName == null) { byPowerName = All.ToDictionary((BossPower p) => p.PowerName); } if (!byPowerName.TryGetValue(name, out var value)) { return null; } return value; } internal static BossPower ByYamlName(string name) { if (string.IsNullOrEmpty(name)) { return null; } if (byYamlName == null) { byYamlName = All.ToDictionary((BossPower p) => p.YamlName); } if (!byYamlName.TryGetValue(name, out var value)) { return null; } return value; } internal static BossPower ByBossKey(string key) { if (string.IsNullOrEmpty(key)) { return null; } BossPower[] all = All; foreach (BossPower bossPower in all) { if (string.Equals(bossPower.BossKey, key, StringComparison.OrdinalIgnoreCase)) { return bossPower; } } return null; } internal static void BindConfigs() { BossPower[] all = All; foreach (BossPower bossPower in all) { PowerFeature[] features = bossPower.Features; for (int j = 0; j < features.Length; j++) { features[j].Bind(bossPower.ConfigCategory); } } } private static BossPower[] Build() { return new BossPower[7] { new BossPower { PowerName = "GP_Eikthyr", PassiveName = "PP_Eikthyr", BossKey = "defeated_eikthyr", YamlName = "Eikthyr", LocKey = "$pp_power_eikthyr", ConfigCategory = "Power 1 - Eikthyr", Features = new PowerFeature[7] { new FloatFeature("$pp_feature_eikthyr_1", "Feature 1 - Run stamina cost", "Fraction by which running stamina drain changes. Negative is cheaper.", -0.3f, -1f, 5f, ValueDisplay.Percent, delegate(PassiveBuild b, float v) { b.Se.m_runStaminaDrainModifier = v; }), new FloatFeature("$pp_feature_eikthyr_2", "Feature 2 - Jump stamina cost", "Fraction by which jump stamina cost changes. Negative is cheaper.", -0.3f, -1f, 5f, ValueDisplay.Percent, delegate(PassiveBuild b, float v) { b.Se.m_jumpStaminaUseModifier = v; }), new FloatFeature("$pp_feature_eikthyr_3", "Feature 3 - Swim stamina cost", "Fraction by which swim stamina cost changes. Negative is cheaper.", -0.3f, -1f, 5f, ValueDisplay.Percent, delegate(PassiveBuild b, float v) { b.Se.m_swimStaminaUseModifier = v; }), new FloatFeature("$pp_feature_eikthyr_4", "Feature 4 - Movement speed", "Fraction added to movement speed.", 0.03f, -1f, 5f, ValueDisplay.Percent, delegate(PassiveBuild b, float v) { b.Se.m_speedModifier = v; }), new FloatFeature("$pp_feature_eikthyr_5", "Feature 5 - Stamina regeneration", "Fraction added to stamina regeneration.", 0.15f, -1f, 5f, ValueDisplay.Percent, delegate(PassiveBuild b, float v) { b.Se.m_staminaRegenMultiplier = 1f + v; }), new FloatFeature("$pp_feature_eikthyr_6", "Feature 6 - Fall damage", "Fraction by which fall damage changes. Negative takes less.", -0.35f, -1f, 5f, ValueDisplay.Percent, delegate(PassiveBuild b, float v) { b.Se.m_fallDamageModifier = v; }), new FloatFeature("$pp_feature_eikthyr_7", "Feature 7 - Dodge stamina cost", "Fraction by which dodge stamina cost changes. Negative is cheaper.", -0.25f, -1f, 5f, ValueDisplay.Percent, delegate(PassiveBuild b, float v) { b.Se.m_dodgeStaminaUseModifier = v; }) } }, new BossPower { PowerName = "GP_TheElder", PassiveName = "PP_TheElder", BossKey = "defeated_gdking", YamlName = "TheElder", LocKey = "$pp_power_theelder", ConfigCategory = "Power 2 - The Elder", Features = new PowerFeature[7] { new FloatFeature("$pp_feature_theelder_1", "Feature 1 - Health regeneration", "Fraction added to health regeneration.", 0.15f, -1f, 5f, ValueDisplay.Percent, delegate(PassiveBuild b, float v) { b.Se.m_healthRegenMultiplier = 1f + v; }), new FloatFeature("$pp_feature_theelder_2", "Feature 2 - Chopping damage", "Fraction added to chop damage against trees.", 0.3f, -1f, 5f, ValueDisplay.Percent, delegate(PassiveBuild b, float v) { b.Se.m_percentigeDamageModifiers.m_chop += v; }), new FloatFeature("$pp_feature_theelder_3", "Feature 3 - Pickaxe damage", "Fraction added to pickaxe damage against rock and ore.", 0.3f, -1f, 5f, ValueDisplay.Percent, delegate(PassiveBuild b, float v) { b.Se.m_percentigeDamageModifiers.m_pickaxe += v; }), new FloatFeature("$pp_feature_theelder_4", "Feature 4 - Carry weight", "Extra carry weight in units.", 75f, 0f, 1000f, ValueDisplay.Flat, delegate(PassiveBuild b, float v) { SE_Stats se = b.Se; se.m_addMaxCarryWeight += v; }), new FloatFeature("$pp_feature_theelder_5", "Feature 5 - Woodcutting skill", "Effective Woodcutting skill levels added.", 12f, 0f, 100f, ValueDisplay.Flat, delegate(PassiveBuild b, float v) { b.AddSkillLevel((SkillType)13, v); }), new FloatFeature("$pp_feature_theelder_6", "Feature 6 - Pickaxes skill", "Effective Pickaxes skill levels added.", 12f, 0f, 100f, ValueDisplay.Flat, delegate(PassiveBuild b, float v) { b.AddSkillLevel((SkillType)12, v); }), new FloatFeature("$pp_feature_theelder_7", "Feature 7 - Regeneration tick", "Health healed every 10 seconds.", 1f, 0f, 50f, ValueDisplay.Flat, delegate(PassiveBuild b, float v) { b.Se.m_tickInterval = 10f; b.Se.m_healthPerTick = v; }) } }, new BossPower { PowerName = "GP_Bonemass", PassiveName = "PP_Bonemass", BossKey = "defeated_bonemass", YamlName = "Bonemass", LocKey = "$pp_power_bonemass", ConfigCategory = "Power 3 - Bonemass", Features = new PowerFeature[7] { new DamageModFeature("$pp_feature_bonemass_1", "Feature 1 - Blunt resistance", "Resistance to blunt damage.", (DamageType)1, (DamageModifier)7), new DamageModFeature("$pp_feature_bonemass_2", "Feature 2 - Slash resistance", "Resistance to slash damage.", (DamageType)2, (DamageModifier)7), new DamageModFeature("$pp_feature_bonemass_3", "Feature 3 - Pierce resistance", "Resistance to pierce damage.", (DamageType)4, (DamageModifier)7), new FloatFeature("$pp_feature_bonemass_4", "Feature 4 - Block stamina cost", "Fraction by which blocking stamina cost changes. Negative is cheaper.", -0.5f, -1f, 5f, ValueDisplay.Percent, delegate(PassiveBuild b, float v) { b.Se.m_blockStaminaUseModifier = v; }), new DamageModFeature("$pp_feature_bonemass_5", "Feature 5 - Poison resistance", "Resistance to poison damage.", (DamageType)256, (DamageModifier)1), new FloatFeature("$pp_feature_bonemass_6", "Feature 6 - Armor", "Flat armor added.", 6f, 0f, 200f, ValueDisplay.Flat, delegate(PassiveBuild b, float v) { SE_Stats se = b.Se; se.m_addArmor += v; }), new FloatFeature("$pp_feature_bonemass_7", "Feature 7 - Stagger taken", "Fraction by which stagger buildup changes. Negative staggers less.", -0.25f, -1f, 5f, ValueDisplay.Percent, delegate(PassiveBuild b, float v) { b.Se.m_staggerModifier = v; }) } }, new BossPower { PowerName = "GP_Moder", PassiveName = "PP_Moder", BossKey = "defeated_dragon", YamlName = "Moder", LocKey = "$pp_power_moder", ConfigCategory = "Power 4 - Moder", Features = new PowerFeature[7] { new DamageModFeature("$pp_feature_moder_1", "Feature 1 - Frost resistance", "Resistance to frost damage.", (DamageType)64, (DamageModifier)1), new AttributeFeature("$pp_feature_moder_2", "Feature 2 - Cold immunity", "Grants the cold resistance attribute, so freezing weather no longer harms you.", (StatusAttribute)1), new AttributeFeature("$pp_feature_moder_3", "Feature 3 - Sailing tailwind", "Grants the sailing power attribute, so ships you crew always have the wind behind them.", (StatusAttribute)4), new FloatFeature("$pp_feature_moder_4", "Feature 4 - Carry weight", "Extra carry weight in units.", 150f, 0f, 1000f, ValueDisplay.Flat, delegate(PassiveBuild b, float v) { SE_Stats se = b.Se; se.m_addMaxCarryWeight += v; }), new FloatFeature("$pp_feature_moder_5", "Feature 5 - Movement speed", "Fraction added to movement speed.", 0.05f, -1f, 5f, ValueDisplay.Percent, delegate(PassiveBuild b, float v) { b.Se.m_speedModifier = v; }), new FloatFeature("$pp_feature_moder_6", "Feature 6 - Wind at your back", "Fraction by which running with the wind speeds you up and drains less stamina.", 0.25f, 0f, 5f, ValueDisplay.Percent, delegate(PassiveBuild b, float v) { b.Se.m_windMovementModifier = v; b.Se.m_windRunStaminaModifier = 0f - v; }), new FloatFeature("$pp_feature_moder_7", "Feature 7 - Swim speed", "Fraction added to swim speed.", 0.25f, -1f, 5f, ValueDisplay.Percent, delegate(PassiveBuild b, float v) { b.Se.m_swimSpeedModifier = v; }) } }, new BossPower { PowerName = "GP_Yagluth", PassiveName = "PP_Yagluth", BossKey = "defeated_goblinking", YamlName = "Yagluth", LocKey = "$pp_power_yagluth", ConfigCategory = "Power 5 - Yagluth", Features = new PowerFeature[7] { new DamageModFeature("$pp_feature_yagluth_1", "Feature 1 - Lightning resistance", "Resistance to lightning damage.", (DamageType)128, (DamageModifier)1), new FloatFeature("$pp_feature_yagluth_2", "Feature 2 - Melee damage", "Fraction added to blunt, slash and pierce damage you deal.", 0.05f, -1f, 5f, ValueDisplay.Percent, delegate(PassiveBuild b, float v) { b.Se.m_percentigeDamageModifiers.m_blunt += v; b.Se.m_percentigeDamageModifiers.m_slash += v; b.Se.m_percentigeDamageModifiers.m_pierce += v; }), new FloatFeature("$pp_feature_yagluth_3", "Feature 3 - Farming skill", "Effective Farming skill levels added.", 12f, 0f, 100f, ValueDisplay.Flat, delegate(PassiveBuild b, float v) { b.AddSkillLevel((SkillType)106, v); }), new FloatFeature("$pp_feature_yagluth_4", "Feature 4 - Elemental damage", "Fraction added to fire, frost, lightning, poison and spirit damage you deal.", 0.05f, -1f, 5f, ValueDisplay.Percent, delegate(PassiveBuild b, float v) { b.Se.m_percentigeDamageModifiers.m_fire += v; b.Se.m_percentigeDamageModifiers.m_frost += v; b.Se.m_percentigeDamageModifiers.m_lightning += v; b.Se.m_percentigeDamageModifiers.m_poison += v; b.Se.m_percentigeDamageModifiers.m_spirit += v; }), new FloatFeature("$pp_feature_yagluth_5", "Feature 5 - Skill gain rate", "Fraction added to how fast every skill is raised.", 0.15f, -1f, 5f, ValueDisplay.Percent, delegate(PassiveBuild b, float v) { //IL_000b: Unknown result type (might be due to invalid IL or missing references) b.Se.m_raiseSkill = (SkillType)999; b.Se.m_raiseSkillModifier = v; }), new FloatFeature("$pp_feature_yagluth_6", "Feature 6 - Armor multiplier", "Fraction added to total armor.", 0.1f, -1f, 5f, ValueDisplay.Percent, delegate(PassiveBuild b, float v) { b.Se.m_armorMultiplier = v; }), new FloatFeature("$pp_feature_yagluth_7", "Feature 7 - Mastery, all damage", "Fraction added to every damage type you deal, on top of features 2 and 4.", 0.05f, -1f, 5f, ValueDisplay.Percent, delegate(PassiveBuild b, float v) { b.Se.m_percentigeDamageModifiers.m_damage += v; b.Se.m_percentigeDamageModifiers.m_blunt += v; b.Se.m_percentigeDamageModifiers.m_slash += v; b.Se.m_percentigeDamageModifiers.m_pierce += v; b.Se.m_percentigeDamageModifiers.m_chop += v; b.Se.m_percentigeDamageModifiers.m_pickaxe += v; b.Se.m_percentigeDamageModifiers.m_fire += v; b.Se.m_percentigeDamageModifiers.m_frost += v; b.Se.m_percentigeDamageModifiers.m_lightning += v; b.Se.m_percentigeDamageModifiers.m_poison += v; b.Se.m_percentigeDamageModifiers.m_spirit += v; }) } }, new BossPower { PowerName = "GP_Queen", PassiveName = "PP_Queen", BossKey = "defeated_queen", YamlName = "Queen", LocKey = "$pp_power_queen", ConfigCategory = "Power 6 - The Queen", Features = new PowerFeature[7] { new DamageModFeature("$pp_feature_queen_1", "Feature 1 - Poison resistance", "Resistance to poison damage.", (DamageType)256, (DamageModifier)1), new FloatFeature("$pp_feature_queen_2", "Feature 2 - Sneak stamina cost", "Fraction by which sneaking stamina cost changes. Negative is cheaper.", -0.5f, -1f, 5f, ValueDisplay.Percent, delegate(PassiveBuild b, float v) { b.Se.m_sneakStaminaUseModifier = v; }), new FloatFeature("$pp_feature_queen_3", "Feature 3 - Eitr regeneration", "Fraction added to eitr regeneration.", 0.5f, -1f, 5f, ValueDisplay.Percent, delegate(PassiveBuild b, float v) { b.Se.m_eitrRegenMultiplier = 1f + v; }), new FloatFeature("$pp_feature_queen_4", "Feature 4 - Stealth", "Fraction added to how hidden you are while sneaking.", 0.25f, -1f, 5f, ValueDisplay.Percent, delegate(PassiveBuild b, float v) { b.Se.m_stealthModifier = v; }), new FloatFeature("$pp_feature_queen_5", "Feature 5 - Noise", "Fraction by which the noise you make changes. Negative is quieter.", -0.25f, -1f, 5f, ValueDisplay.Percent, delegate(PassiveBuild b, float v) { b.Se.m_noiseModifier = v; }), new FloatFeature("$pp_feature_queen_6", "Feature 6 - Elemental Magic skill", "Effective Elemental Magic skill levels added.", 12f, 0f, 100f, ValueDisplay.Flat, delegate(PassiveBuild b, float v) { b.AddSkillLevel((SkillType)9, v); }), new FloatFeature("$pp_feature_queen_7", "Feature 7 - Blood Magic skill", "Effective Blood Magic skill levels added.", 12f, 0f, 100f, ValueDisplay.Flat, delegate(PassiveBuild b, float v) { b.AddSkillLevel((SkillType)10, v); }) } }, new BossPower { PowerName = "GP_Fader", PassiveName = "PP_Fader", BossKey = "defeated_fader", YamlName = "Fader", LocKey = "$pp_power_fader", ConfigCategory = "Power 7 - Fader", Features = new PowerFeature[7] { new DamageModFeature("$pp_feature_fader_1", "Feature 1 - Fire resistance", "Resistance to fire damage.", (DamageType)32, (DamageModifier)1), new FloatFeature("$pp_feature_fader_2", "Feature 2 - Adrenaline gain", "Fraction added to adrenaline gained in combat.", 0.5f, -1f, 5f, ValueDisplay.Percent, delegate(PassiveBuild b, float v) { b.Se.m_adrenalineModifier = v; }), new FloatFeature("$pp_feature_fader_3", "Feature 3 - Stagger taken", "Fraction by which stagger buildup changes. Negative staggers less.", -0.25f, -1f, 5f, ValueDisplay.Percent, delegate(PassiveBuild b, float v) { b.Se.m_staggerModifier = v; }), new FloatFeature("$pp_feature_fader_4", "Feature 4 - Attack stamina cost", "Fraction by which attack stamina cost changes. Negative is cheaper.", -0.2f, -1f, 5f, ValueDisplay.Percent, delegate(PassiveBuild b, float v) { b.Se.m_attackStaminaUseModifier = v; }), new FloatFeature("$pp_feature_fader_5", "Feature 5 - Armor", "Flat armor added.", 8f, 0f, 200f, ValueDisplay.Flat, delegate(PassiveBuild b, float v) { SE_Stats se = b.Se; se.m_addArmor += v; }), new FloatFeature("$pp_feature_fader_6", "Feature 6 - Timed block bonus", "Fraction added to the parry bonus of a timed block.", 0.5f, -1f, 5f, ValueDisplay.Percent, delegate(PassiveBuild b, float v) { b.Se.m_timedBlockBonus = v; }), new FloatFeature("$pp_feature_fader_7", "Feature 7 - Health regeneration", "Fraction added to health regeneration.", 0.2f, -1f, 5f, ValueDisplay.Percent, delegate(PassiveBuild b, float v) { b.Se.m_healthRegenMultiplier = 1f + v; }) } } }; } } [BepInPlugin("MidnightsFX.ProgressivePowers", "ProgressivePowers", "0.1.0")] [BepInDependency(/*Could not decode attribute arguments.*/)] [NetworkCompatibility(/*Could not decode attribute arguments.*/)] internal class ProgressivePowers : BaseUnityPlugin { public const string PluginGUID = "MidnightsFX.ProgressivePowers"; public const string PluginName = "ProgressivePowers"; public const string PluginVersion = "0.1.0"; internal static ManualLogSource Log; internal ValConfig cfg; public static CustomLocalization Localization = LocalizationManager.Instance.GetLocalization(); private const float RefreshInterval = 2f; private Harmony harmony; public void Awake() { //IL_0061: Unknown result type (might be due to invalid IL or missing references) //IL_006b: Expected O, but got Unknown Log = ((BaseUnityPlugin)this).Logger; cfg = new ValConfig(((BaseUnityPlugin)this).Config); LocalizationLoader.AddLocalizations(); LevelRequirements.Initialize(); PrefabManager.OnVanillaPrefabsAvailable += PassivePowerManager.BuildPassiveEffects; SynchronizationManager.OnConfigurationSynchronized += delegate { ValConfig.MarkChanged(); PassivePowerManager.Refresh(force: true); }; harmony = new Harmony("MidnightsFX.ProgressivePowers"); harmony.PatchAll(typeof(ProgressivePowers).Assembly); ((MonoBehaviour)this).InvokeRepeating("SafetyNetTick", 2f, 2f); ValConfig.SaveOnSet(enabled: true); } private void SafetyNetTick() { PassivePowerManager.Refresh(); } public void OnDestroy() { ((MonoBehaviour)this).CancelInvoke("SafetyNetTick"); PrefabManager.OnVanillaPrefabsAvailable -= PassivePowerManager.BuildPassiveEffects; LevelRequirements.Shutdown(); Harmony obj = harmony; if (obj != null) { obj.UnpatchSelf(); } } } } namespace ProgressivePowers.Patches { [HarmonyPatch(typeof(Player), "StartGuardianPower")] internal static class BlockStartGuardianPower { private static bool Prefix(Player __instance, ref bool __result) { if (!ValConfig.BlockPowerActivation.Value) { return true; } __result = false; if ((Object)(object)__instance == (Object)(object)Player.m_localPlayer) { ((Character)__instance).Message((MessageType)2, "$pp_cannot_activate", 0, (Sprite)null, false); } return false; } } [HarmonyPatch(typeof(Player), "ActivateGuardianPower")] internal static class BlockActivateGuardianPower { private static bool Prefix(ref bool __result) { if (!ValConfig.BlockPowerActivation.Value) { return true; } __result = false; return false; } } internal static class KillPersistence { [HarmonyPatch(typeof(Player), "Load")] internal static class LoadKills { private static void Postfix(Player __instance) { if (!((Object)(object)__instance != (Object)(object)Player.m_localPlayer)) { KillTracker.Bind(__instance); } } } [HarmonyPatch(typeof(Player), "Save")] internal static class SaveKills { private static void Prefix(Player __instance) { if (!((Object)(object)__instance != (Object)(object)Player.m_localPlayer)) { KillTracker.Flush(__instance); } } } } [HarmonyPatch(typeof(Player), "SetGuardianPower")] internal static class OnSetGuardianPower { private static void Postfix(Player __instance) { if (!((Object)(object)__instance != (Object)(object)Player.m_localPlayer)) { __instance.m_guardianPowerCooldown = 0f; PassivePowerManager.Refresh(force: true); } } } [HarmonyPatch(typeof(Player), "OnSpawned")] internal static class OnPlayerSpawned { private static void Postfix(Player __instance) { if (!((Object)(object)__instance != (Object)(object)Player.m_localPlayer)) { __instance.m_guardianPowerCooldown = 0f; KillTracker.EnsureLoaded(__instance); PassivePowerManager.Refresh(force: true); } } } [HarmonyPatch(typeof(Player), "OnRespawn")] internal static class OnPlayerRespawn { private static void Postfix(Player __instance) { if (!((Object)(object)__instance != (Object)(object)Player.m_localPlayer)) { PassivePowerManager.Refresh(force: true); } } } internal static class KillBroadcast { [HarmonyPatch(typeof(Game), "Start")] internal static class RegisterKillRpc { private static void Postfix() { ZRoutedRpc.instance.Register<string, Vector3>("MidnightsFX.ProgressivePowers.BossKill", (Action<long, string, Vector3>)OnBossKill); } } internal struct KillSite { internal bool Owned; internal Vector3 Position; } [HarmonyPatch(typeof(Character), "OnDeath")] internal static class CountBossKills { private static void Prefix(Character __instance, out KillSite __state) { //IL_003f: Unknown result type (might be due to invalid IL or missing references) //IL_0044: Unknown result type (might be due to invalid IL or missing references) __state = default(KillSite); if (!string.IsNullOrEmpty(__instance.m_defeatSetGlobalKey) && !((Object)(object)__instance.m_nview == (Object)null) && __instance.m_nview.IsOwner()) { __state.Owned = true; __state.Position = ((Component)__instance).transform.position; } } private static void Postfix(Character __instance, KillSite __state) { //IL_0037: Unknown result type (might be due to invalid IL or missing references) if (__state.Owned) { BossPower bossPower = PowerDefinitions.ByBossKey(__instance.m_defeatSetGlobalKey); if (bossPower != null) { ZRoutedRpc.instance.InvokeRoutedRPC(0L, "MidnightsFX.ProgressivePowers.BossKill", new object[2] { bossPower.YamlName, __state.Position }); } } } } private const string RpcName = "MidnightsFX.ProgressivePowers.BossKill"; private static void OnBossKill(long sender, string yamlName, Vector3 pos) { //IL_0034: Unknown result type (might be due to invalid IL or missing references) //IL_0039: Unknown result type (might be due to invalid IL or missing references) BossPower bossPower = PowerDefinitions.ByYamlName(yamlName); if (bossPower == null) { return; } Player localPlayer = Player.m_localPlayer; if (!((Object)(object)localPlayer == (Object)null)) { float value = ValConfig.KillCreditRange.Value; if (value > 0f && Vector3.Distance(((Component)localPlayer).transform.position, pos) > value) { Logger.LogDebug($"{bossPower.YamlName} died outside the {value}m credit range; no kill recorded."); return; } KillTracker.RecordKill(localPlayer, bossPower); PassivePowerManager.Refresh(force: true); } } } [HarmonyPatch(typeof(SE_Stats), "GetTooltipString")] internal static class PowerTooltipText { private static void Postfix(SE_Stats __instance, ref string __result) { BossPower bossPower = PowerTooltips.Match(((Object)__instance).name); if (bossPower != null) { __result = PowerTooltips.Build(bossPower); } } } [HarmonyPatch(typeof(ItemStand), "GetHoverText")] internal static class BossStoneHoverText { private static void Postfix(ItemStand __instance, ref string __result) { //IL_003d: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)__instance.m_guardianPower == (Object)null) && !__instance.m_canBeRemoved && !((Object)(object)Player.m_localPlayer == (Object)null) && !string.IsNullOrEmpty(__result) && __instance.HaveAttachment() && PrivateArea.CheckAccess(((Component)__instance).transform.position, 0f, false, false)) { BossPower bossPower = PowerDefinitions.ByPowerName(((Object)__instance.m_guardianPower).name); if (bossPower != null) { bool flag = Player.m_localPlayer.GetGuardianPowerName() == ((Object)__instance.m_guardianPower).name; string text = Localization.instance.Localize("<color=orange>" + __instance.m_guardianPower.m_name + "</color>"); string text2 = Localization.instance.Localize(flag ? "$pp_attuned" : "[<color=yellow><b>$KEY_Use</b></color>] $pp_attune"); __result = text + "\n" + PowerTooltips.Build(bossPower) + "\n\n" + text2; } } } } [HarmonyPatch(typeof(Hud), "UpdateGuardianPower")] internal static class GuardianPowerHudLevel { private static void Postfix(Hud __instance, Player player) { //IL_0087: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)player == (Object)null) && !((Object)(object)player != (Object)(object)Player.m_localPlayer) && PassivePowerManager.CurrentPower != null && !((Object)(object)__instance.m_gpRoot == (Object)null) && ((Component)__instance.m_gpRoot).gameObject.activeSelf) { if ((Object)(object)__instance.m_gpCooldown != (Object)null) { __instance.m_gpCooldown.text = $"{PassivePowerManager.CurrentLevel}/{7}"; } if ((Object)(object)__instance.m_gpIcon != (Object)null) { ((Graphic)__instance.m_gpIcon).color = Color.white; } } } } } namespace ProgressivePowers.Modules { internal enum ValueDisplay { Percent, Flat } internal sealed class PassiveBuild { internal readonly SE_Stats Se; private int usedSkillSlots; internal PassiveBuild(SE_Stats se) { Se = se; } internal void AddDamageMod(DamageType type, DamageModifier modifier) { //IL_000d: Unknown result type (might be due to invalid IL or missing references) //IL_0015: Unknown result type (might be due to invalid IL or missing references) //IL_0016: Unknown result type (might be due to invalid IL or missing references) //IL_001d: Unknown result type (might be due to invalid IL or missing references) //IL_001e: Unknown result type (might be due to invalid IL or missing references) //IL_0023: Unknown result type (might be due to invalid IL or missing references) Se.m_mods.Add(new DamageModPair { m_type = type, m_modifier = modifier }); } internal void AddSkillLevel(SkillType skill, float modifier) { //IL_000e: Unknown result type (might be due to invalid IL or missing references) //IL_000f: Unknown result type (might be due to invalid IL or missing references) //IL_0061: Unknown result type (might be due to invalid IL or missing references) //IL_0037: Unknown result type (might be due to invalid IL or missing references) //IL_0038: Unknown result type (might be due to invalid IL or missing references) if (usedSkillSlots == 0) { Se.m_skillLevel = skill; Se.m_skillLevelModifier = modifier; usedSkillSlots = 1; } else if (usedSkillSlots == 1) { Se.m_skillLevel2 = skill; Se.m_skillLevelModifier2 = modifier; usedSkillSlots = 2; } else { Logger.LogWarning($"{((Object)Se).name}: more than two skill-level features are active; '{skill}' was dropped. SE_Stats only has two slots."); } } } internal abstract class PowerFeature { internal string LocKey; internal string ConfigKey; internal string Description; internal abstract bool Enabled { get; } internal abstract void Bind(string category); internal abstract void Apply(PassiveBuild build); internal abstract string ValueText(); internal string Describe() { return Localization.instance.Localize(LocKey).Replace("$1", ValueText()); } protected static string Signed(float value, ValueDisplay display) { float num = ((display == ValueDisplay.Percent) ? (value * 100f) : value); string arg = ((num > 0f) ? "+" : ""); if (display != ValueDisplay.Percent) { return $"{arg}{num:0.#}"; } return $"{arg}{num:0.#}%"; } } internal sealed class FloatFeature : PowerFeature { private readonly float defaultValue; private readonly float min; private readonly float max; private readonly ValueDisplay display; private readonly Action<PassiveBuild, float> apply; internal ConfigEntry<float> Cfg; internal override bool Enabled { get { if (Cfg != null) { return Mathf.Abs(Cfg.Value) > 0.0001f; } return false; } } internal FloatFeature(string locKey, string configKey, string description, float defaultValue, float min, float max, ValueDisplay display, Action<PassiveBuild, float> apply) { LocKey = locKey; ConfigKey = configKey; Description = description; this.defaultValue = defaultValue; this.min = min; this.max = max; this.display = display; this.apply = apply; } internal override void Bind(string category) { Cfg = ValConfig.BindServerConfig(category, ConfigKey, defaultValue, Description + " Set to 0 to disable.", advanced: false, min, max); } internal override void Apply(PassiveBuild build) { apply(build, Cfg.Value); } internal override string ValueText() { return PowerFeature.Signed((Cfg == null) ? defaultValue : Cfg.Value, display); } } internal sealed class DamageModFeature : PowerFeature { internal static readonly string[] Choices = new string[5] { "Normal", "SlightlyResistant", "Resistant", "VeryResistant", "Immune" }; private readonly DamageType damageType; private readonly string defaultValue; internal ConfigEntry<string> Cfg; internal override bool Enabled => (int)Current() > 0; internal unsafe DamageModFeature(string locKey, string configKey, string description, DamageType damageType, DamageModifier defaultValue) { //IL_001c: Unknown result type (might be due to invalid IL or missing references) //IL_001e: Unknown result type (might be due to invalid IL or missing references) LocKey = locKey; ConfigKey = configKey; Description = description; this.damageType = damageType; this.defaultValue = ((object)(*(DamageModifier*)(&defaultValue))/*cast due to .constrained prefix*/).ToString(); } internal override void Bind(string category) { Cfg = ValConfig.BindServerConfig(category, ConfigKey, defaultValue, Description + " Set to Normal to disable.", new AcceptableValueList<string>(Choices)); } private DamageModifier Current() { //IL_0026: Unknown result type (might be due to invalid IL or missing references) if (!Enum.TryParse<DamageModifier>((Cfg == null) ? defaultValue : Cfg.Value, out DamageModifier result)) { return (DamageModifier)0; } return result; } internal override void Apply(PassiveBuild build) { //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_0008: Unknown result type (might be due to invalid IL or missing references) build.AddDamageMod(damageType, Current()); } internal override string ValueText() { //IL_000b: Unknown result type (might be due to invalid IL or missing references) //IL_0010: Unknown result type (might be due to invalid IL or missing references) return Localization.instance.Localize("$pp_dmgmod_" + ((object)Current()/*cast due to .constrained prefix*/).ToString().ToLowerInvariant()); } } internal sealed class AttributeFeature : PowerFeature { private readonly StatusAttribute attribute; private readonly bool defaultValue; internal ConfigEntry<bool> Cfg; internal override bool Enabled { get { if (Cfg != null) { return Cfg.Value; } return defaultValue; } } internal AttributeFeature(string locKey, string configKey, string description, StatusAttribute attribute, bool defaultValue = true) { //IL_001c: Unknown result type (might be due to invalid IL or missing references) //IL_001e: Unknown result type (might be due to invalid IL or missing references) LocKey = locKey; ConfigKey = configKey; Description = description; this.attribute = attribute; this.defaultValue = defaultValue; } internal override void Bind(string category) { Cfg = ValConfig.BindServerConfig(category, ConfigKey, defaultValue, Description); } internal override void Apply(PassiveBuild build) { //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_000d: Unknown result type (might be due to invalid IL or missing references) //IL_0012: Unknown result type (might be due to invalid IL or missing references) //IL_0013: Unknown result type (might be due to invalid IL or missing references) SE_Stats se = build.Se; ((StatusEffect)se).m_attributes = (StatusAttribute)(((StatusEffect)se).m_attributes | attribute); } internal override string ValueText() { return ""; } } internal static class PowerTooltips { private const string LockedColor = "#808080"; internal static BossPower Match(string statusEffectName) { if (string.IsNullOrEmpty(statusEffectName)) { return null; } BossPower[] all = PowerDefinitions.All; foreach (BossPower bossPower in all) { if (statusEffectName == bossPower.PowerName || statusEffectName == bossPower.PassiveName) { return bossPower; } } return null; } internal static string Build(BossPower power) { int level = LevelRequirements.GetLevel(power); StringBuilder stringBuilder = new StringBuilder(); stringBuilder.Append("<color=orange>").Append(Localize("$pp_level").Replace("$1", level.ToString()).Replace("$2", 7.ToString())).Append("</color>\n"); bool value = ValConfig.ShowLockedFeatures.Value; for (int i = 0; i < power.Features.Length; i++) { PowerFeature powerFeature = power.Features[i]; if (!powerFeature.Enabled) { continue; } if (level >= i + 1) { stringBuilder.Append('\n').Append(powerFeature.Describe()); } else if (value) { stringBuilder.Append("\n<color=").Append("#808080").Append('>') .Append(powerFeature.Describe()); string text = DescribeOutstanding(power, i + 1); if (text.Length > 0) { stringBuilder.Append(" ").Append(text); } stringBuilder.Append("</color>"); } } return stringBuilder.ToString(); } private static string DescribeOutstanding(BossPower power, int level) { Dictionary<string, int> dictionary = LevelRequirements.Outstanding(power, level); if (dictionary.Count == 0) { return ""; } StringBuilder stringBuilder = new StringBuilder(); foreach (KeyValuePair<string, int> item in dictionary) { if (stringBuilder.Length > 0) { stringBuilder.Append(", "); } BossPower bossPower = PowerDefinitions.ByYamlName(item.Key); stringBuilder.Append((bossPower != null) ? Localize(bossPower.LocKey) : item.Key).Append(" x").Append(item.Value); } return Localize("$pp_needs").Replace("$1", stringBuilder.ToString()); } private static string Localize(string token) { if (Localization.instance == null) { return token; } return Localization.instance.Localize(token); } } } namespace ProgressivePowers.Common { internal static class ConfigChangeDebouncer { private static readonly Dictionary<object, Action> pendingActions = new Dictionary<object, Action>(); private static readonly Dictionary<object, float> fireAt = new Dictionary<object, float>(); private static readonly HashSet<object> running = new HashSet<object>(); internal static void Schedule(object key, Action action) { float num = ((ValConfig.ConfigApplyDelay != null) ? ValConfig.ConfigApplyDelay.Value : 0f); if (num <= 0f) { action(); return; } ThreadingHelper instance = ThreadingHelper.Instance; if (!((Object)(object)instance == (Object)null)) { pendingActions[key] = action; fireAt[key] = Time.realtimeSinceStartup + num; if (!running.Contains(key)) { running.Add(key); ((MonoBehaviour)instance).StartCoroutine(Run(key)); } } } private static IEnumerator Run(object key) { float value; while (fireAt.TryGetValue(key, out value) && Time.realtimeSinceStartup < value) { yield return null; } pendingActions.TryGetValue(key, out var value2); pendingActions.Remove(key); fireAt.Remove(key); running.Remove(key); value2?.Invoke(); } } internal static class ConfigDrawHelpers { private static readonly Dictionary<object, string> TextBuffer = new Dictionary<object, string>(); private static readonly Dictionary<object, float> SliderPending = new Dictionary<object, float>(); private static GUIStyle _headerButton; private static GUIStyle _groupLabel; private static GUIStyle _dim; internal static GUIStyle HeaderButton => _headerButton; internal static GUIStyle Dim => _dim; internal static void EnsureStyles() { //IL_0012: Unknown result type (might be due to invalid IL or missing references) //IL_0017: Unknown result type (might be due to invalid IL or missing references) //IL_001e: Unknown result type (might be due to invalid IL or missing references) //IL_002a: Expected O, but got Unknown //IL_0034: Unknown result type (might be due to invalid IL or missing references) //IL_0039: Unknown result type (might be due to invalid IL or missing references) //IL_0045: Expected O, but got Unknown //IL_004f: Unknown result type (might be due to invalid IL or missing references) //IL_0054: Unknown result type (might be due to invalid IL or missing references) //IL_005b: Unknown result type (might be due to invalid IL or missing references) //IL_0068: Expected O, but got Unknown if (_headerButton == null) { _headerButton = new GUIStyle(GUI.skin.button) { alignment = (TextAnchor)3, fontStyle = (FontStyle)1 }; _groupLabel = new GUIStyle(GUI.skin.label) { fontStyle = (FontStyle)1 }; _dim = new GUIStyle(GUI.skin.label) { fontStyle = (FontStyle)2, fontSize = 11 }; } } internal static ConfigurationManagerAttributes GetAttributes(ConfigEntryBase entry) { if (entry == null) { return null; } ConfigDescription description = entry.Description; if (description == null) { return null; } return description.Tags?.OfType<ConfigurationManagerAttributes>().FirstOrDefault(); } internal static void Hide(ConfigEntryBase entry) { ConfigurationManagerAttributes attributes = GetAttributes(entry); if (attributes != null) { attributes.Browsable = false; } } internal static void GroupHeader(string text) { GUILayout.Space(4f); GUILayout.Label(text, _groupLabel, Array.Empty<GUILayoutOption>()); } private static bool EnterPressed() { //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_000b: Invalid comparison between Unknown and I4 //IL_0012: Unknown result type (might be due to invalid IL or missing references) //IL_0019: Invalid comparison between Unknown and I4 //IL_0020: Unknown result type (might be due to invalid IL or missing references) //IL_002a: Invalid comparison between Unknown and I4 if ((int)Event.current.type == 4) { if ((int)Event.current.keyCode != 13) { return (int)Event.current.keyCode == 271; } return true; } return false; } internal static void DrawBool(string label, ConfigEntry<bool> cfg) { bool flag = GUILayout.Toggle(cfg.Value, " " + label, Array.Empty<GUILayoutOption>()); if (flag != cfg.Value) { cfg.Value = flag; } } internal static void DrawString(string label, ConfigEntry<string> cfg) { GUILayout.BeginHorizontal(Array.Empty<GUILayoutOption>()); GUILayout.Label(label, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(230f) }); string text = "str_" + ((object)cfg).GetHashCode(); GUI.SetNextControlName(text); bool flag = GUI.GetNameOfFocusedControl() == text; string value; string text2 = GUILayout.TextField((flag && TextBuffer.TryGetValue(cfg, out value)) ? value : cfg.Value, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(230f) }); string value2; if (flag) { TextBuffer[cfg] = text2; if (EnterPressed() && cfg.Value != text2) { cfg.Value = text2; } } else if (TextBuffer.TryGetValue(cfg, out value2)) { if (cfg.Value != value2) { cfg.Value = value2; } TextBuffer.Remove(cfg); } GUILayout.EndHorizontal(); } internal static void DrawChoice(string label, ConfigEntry<string> cfg) { GUILayout.BeginHorizontal(Array.Empty<GUILayoutOption>()); GUILayout.Label(label, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(230f) }); if (((ConfigEntryBase)cfg).Description.AcceptableValues is AcceptableValueList<string> val && val.AcceptableValues.Length != 0) { string[] acceptableValues = val.AcceptableValues; int num = Array.IndexOf(acceptableValues, cfg.Value); if (num < 0) { num = 0; } if (GUILayout.Button("◄", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(26f) })) { cfg.Value = acceptableValues[(num - 1 + acceptableValues.Length) % acceptableValues.Length]; } GUILayout.Label(cfg.Value, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(160f) }); if (GUILayout.Button("►", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(26f) })) { cfg.Value = acceptableValues[(num + 1) % acceptableValues.Length]; } } else { GUILayout.Label(cfg.Value, Array.Empty<GUILayoutOption>()); } GUILayout.EndHorizontal(); } internal static void DrawFloat(string label, ConfigEntry<float> cfg) { float num = 0f; float num2 = Mathf.Max(100f, cfg.Value); if (((ConfigEntryBase)cfg).Description.AcceptableValues is AcceptableValueRange<float> val) { num = val.MinValue; num2 = val.MaxValue; } float num3 = DrawSliderRow(label, cfg, cfg.Value, num, num2, isInt: false); if (num3 != cfg.Value) { cfg.Value = Mathf.Clamp(num3, num, num2); } } internal static void DrawInt(string label, ConfigEntry<int> cfg) { float num = 0f; float num2 = Mathf.Max(100f, (float)cfg.Value); if (((ConfigEntryBase)cfg).Description.AcceptableValues is AcceptableValueRange<int> val) { num = val.MinValue; num2 = val.MaxValue; } int num3 = Mathf.Clamp(Mathf.RoundToInt(DrawSliderRow(label, cfg, cfg.Value, num, num2, isInt: true)), (int)num, (int)num2); if (num3 != cfg.Value) { cfg.Value = num3; } } private static float DrawSliderRow(string label, object key, float current, float min, float max, bool isInt) { float num = current; GUILayout.BeginHorizontal(Array.Empty<GUILayoutOption>()); GUILayout.Label(label, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(230f) }); float value; float num2 = (SliderPending.TryGetValue(key, out value) ? value : current); float num3 = GUILayout.HorizontalSlider(num2, min, max, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(150f) }); if (Mathf.Abs(num3 - num2) > Mathf.Epsilon) { SliderPending[key] = num3; } if (SliderPending.TryGetValue(key, out var value2)) { num = value2; if (!Input.GetMouseButton(0)) { SliderPending.Remove(key); } } string text = "num_" + key.GetHashCode(); GUI.SetNextControlName(text); bool flag = GUI.GetNameOfFocusedControl() == text; string text2 = (isInt ? Mathf.RoundToInt(num).ToString() : num.ToString("0.###")); string value3; string text3 = GUILayout.TextField((flag && TextBuffer.TryGetValue(key, out value3)) ? value3 : text2, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(70f) }); string value4; if (flag) { TextBuffer[key] = text3; if (EnterPressed() && float.TryParse(text3, out var result)) { num = result; } } else if (TextBuffer.TryGetValue(key, out value4)) { if (float.TryParse(value4, out var result2)) { num = result2; } TextBuffer.Remove(key); } GUILayout.Label("[" + (isInt ? min.ToString() : min.ToString("0.#")) + " - " + (isInt ? max.ToString() : max.ToString("0.#")) + "]", _dim, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(90f) }); GUILayout.EndHorizontal(); return num; } } internal static class LocalizationLoader { internal static string LocalizationFolder = "Localizations"; internal static void AddLocalizations() { CustomLocalization localization = ProgressivePowers.Localization; string text = Path.Combine(Paths.ConfigPath, "ProgressivePowers", LocalizationFolder); Directory.CreateDirectory(text); string[] manifestResourceNames = Assembly.GetExecutingAssembly().GetManifestResourceNames(); foreach (string text2 in manifestResourceNames) { if (!text2.Contains(LocalizationFolder)) { continue; } string text3 = Regex.Replace(ReadEmbeddedResourceFile(text2), "\\/\\/.*", ""); Dictionary<string, string> internal_localization = SimpleJson.DeserializeObject<Dictionary<string, string>>(text3); string[] array = text2.Split(new char[1] { '.' }); if (File.Exists(text + "/" + array[2] + ".json")) { string text4 = File.ReadAllText(text + "/" + array[2] + ".json"); try { Dictionary<string, string> dictionary = SimpleJson.DeserializeObject<Dictionary<string, string>>(text4); UpdateLocalizationWithMissingKeys(internal_localization, dictionary); Logger.LogDebug("Reading " + text + "/" + array[2] + ".json"); File.WriteAllText(text + "/" + array[2] + ".json", SimpleJson.SerializeObject((object)dictionary)); string text5 = File.ReadAllText(text + "/" + array[2] + ".json"); localization.AddJsonFile(array[2], text5); } catch { File.WriteAllText(text + "/" + array[2] + ".json", text3); Logger.LogDebug("Reading " + text2); localization.AddJsonFile(array[2], text3); } } else { File.WriteAllText(text + "/" + array[2] + ".json", text3); Logger.LogDebug("Reading " + text2); localization.AddJsonFile(array[2], text3); } Logger.LogDebug("Added localization: '" + array[2] + "'"); } } private static Dictionary<string, string> UpdateLocalizationWithMissingKeys(Dictionary<string, string> internal_localization, Dictionary<string, string> cached_localization) { if (internal_localization.Keys != cached_localization.Keys) { List<string> list = cached_localization.Keys.ToList(); foreach (KeyValuePair<string, string> item in internal_localization) { list.Remove(item.Key); if (!cached_localization.ContainsKey(item.Key)) { Logger.LogDebug("Adding missing localization key " + item.Key); cached_localization.Add(item.Key, item.Value); } } if (list.Count > 0) { Logger.LogDebug("Removing extra keys " + string.Join(",", list) + "."); foreach (string item2 in list) { cached_localization.Remove(item2); } } } return cached_localization; } private static string ReadEmbeddedResourceFile(string filename) { using Stream stream = Assembly.GetExecutingAssembly().GetManifestResourceStream(filename); using StreamReader streamReader = new StreamReader(stream); return streamReader.ReadToEnd(); } } }