using 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 EnableDebugMode; public static ConfigEntry DebugLevelOverride; public static ConfigEntry ConfigApplyDelay; public static ConfigEntry KillCreditRange; public static ConfigEntry ShowLockedFeatures; public static ConfigEntry ShowPassiveInStatusBar; public static ConfigEntry BlockPowerActivation; public static ConfigEntry 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("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("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(-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("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 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(category, key, value, new ConfigDescription(description, (AcceptableValueBase)(object)new AcceptableValueRange(valMin, valMax), new object[1] { (object)new ConfigurationManagerAttributes { IsAdminOnly = true, IsAdvanced = advanced } })); } public static ConfigEntry 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(category, key, value, new ConfigDescription(description, acceptableValues, new object[1] { (object)new ConfigurationManagerAttributes { IsAdminOnly = true, IsAdvanced = advanced } })); } public static ConfigEntry 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(category, key, value, new ConfigDescription(description, (AcceptableValueBase)(object)new AcceptableValueRange(valMin, valMax), new object[1] { (object)new ConfigurationManagerAttributes { IsAdminOnly = true, IsAdvanced = advanced } })); } public static ConfigEntry 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(category, key, value, new ConfigDescription(description, (AcceptableValueBase)(object)new AcceptableValueRange(valMin, valMax), new object[1] { (object)new ConfigurationManagerAttributes { IsAdminOnly = true, IsAdvanced = advanced } })); } public static ConfigEntry BindServerConfig(string category, string key, string value, string description, AcceptableValueList 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(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 counts = new Dictionary(); private static Player boundPlayer; private static bool loaded; private static bool reportedFresh; internal static IEnumerable> 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 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>> table = new Dictionary>>(); 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>> dictionary = Parse(value); if (dictionary != null) { table = dictionary; } } private static Dictionary>> Parse(string text) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) Dictionary>> dictionary; try { dictionary = new DeserializerBuilder().Build().Deserialize>>>(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>> dictionary2 = new Dictionary>>(); foreach (KeyValuePair>> item in dictionary) { if (PowerDefinitions.ByYamlName(item.Key) == null) { Logger.LogWarning("ProgressivePowers.Levels.yaml: unknown power '" + item.Key + "', ignored."); continue; } Dictionary> dictionary3 = new Dictionary>(); foreach (KeyValuePair> item2 in item.Value ?? new Dictionary>()) { 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 dictionary4 = new Dictionary(); foreach (KeyValuePair item3 in item2.Value ?? new Dictionary()) { 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 list = new List(); 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>> 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 CumulativeRequirement(BossPower power, int level) { Dictionary dictionary = new Dictionary(); 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 item in value2) { if (!dictionary.TryGetValue(item.Key, out var value3) || item.Value > value3) { dictionary[item.Key] = item.Value; } } } return dictionary; } internal static Dictionary Outstanding(BossPower power, int level) { Dictionary dictionary = new Dictionary(); foreach (KeyValuePair 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> DefaultLevels(int powerIndex) { BossPower[] all = PowerDefinitions.All; string yamlName = all[powerIndex].YamlName; string yamlName2 = all[^1].YamlName; Dictionary dictionary = new Dictionary(); List> list = new List>(); 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(key, value)); } return list; } private static Dictionary>> BuildDefaultTable() { Dictionary>> dictionary = new Dictionary>>(); for (int i = 0; i < PowerDefinitions.All.Length; i++) { Dictionary> dictionary2 = new Dictionary>(); List> list = DefaultLevels(i); for (int j = 0; j < list.Count; j++) { dictionary2[j + 1] = new Dictionary { { 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> 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(); ((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(); 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 byPowerName; private static Dictionary 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("MidnightsFX.ProgressivePowers.BossKill", (Action)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("" + __instance.m_guardianPower.m_name + ""); string text2 = Localization.instance.Localize(flag ? "$pp_attuned" : "[$KEY_Use] $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 apply; internal ConfigEntry 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 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 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(Choices)); } private DamageModifier Current() { //IL_0026: Unknown result type (might be due to invalid IL or missing references) if (!Enum.TryParse((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 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("").Append(Localize("$pp_level").Replace("$1", level.ToString()).Replace("$2", 7.ToString())).Append("\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') .Append(powerFeature.Describe()); string text = DescribeOutstanding(power, i + 1); if (text.Length > 0) { stringBuilder.Append(" ").Append(text); } stringBuilder.Append(""); } } return stringBuilder.ToString(); } private static string DescribeOutstanding(BossPower power, int level) { Dictionary dictionary = LevelRequirements.Outstanding(power, level); if (dictionary.Count == 0) { return ""; } StringBuilder stringBuilder = new StringBuilder(); foreach (KeyValuePair 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 pendingActions = new Dictionary(); private static readonly Dictionary fireAt = new Dictionary(); private static readonly HashSet running = new HashSet(); 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 TextBuffer = new Dictionary(); private static readonly Dictionary SliderPending = new Dictionary(); 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().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()); } 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 cfg) { bool flag = GUILayout.Toggle(cfg.Value, " " + label, Array.Empty()); if (flag != cfg.Value) { cfg.Value = flag; } } internal static void DrawString(string label, ConfigEntry cfg) { GUILayout.BeginHorizontal(Array.Empty()); 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 cfg) { GUILayout.BeginHorizontal(Array.Empty()); GUILayout.Label(label, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(230f) }); if (((ConfigEntryBase)cfg).Description.AcceptableValues is AcceptableValueList 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()); } GUILayout.EndHorizontal(); } internal static void DrawFloat(string label, ConfigEntry cfg) { float num = 0f; float num2 = Mathf.Max(100f, cfg.Value); if (((ConfigEntryBase)cfg).Description.AcceptableValues is AcceptableValueRange 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 cfg) { float num = 0f; float num2 = Mathf.Max(100f, (float)cfg.Value); if (((ConfigEntryBase)cfg).Description.AcceptableValues is AcceptableValueRange 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()); 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 internal_localization = SimpleJson.DeserializeObject>(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 dictionary = SimpleJson.DeserializeObject>(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 UpdateLocalizationWithMissingKeys(Dictionary internal_localization, Dictionary cached_localization) { if (internal_localization.Keys != cached_localization.Keys) { List list = cached_localization.Keys.ToList(); foreach (KeyValuePair 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(); } } }