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 CustomDifficulty v2.1.1
BepInEx\plugins\CustomDifficulty\CustomDifficulty.dll
Decompiled a week ago
The result has been truncated due to the large size, download it to view full contents!
using System; using System.Collections.Generic; using System.IO; using System.Reflection; using System.Runtime.CompilerServices; using BepInEx; using BepInEx.Configuration; using GlobalEnums; using HarmonyLib; using Newtonsoft.Json; using Newtonsoft.Json.Linq; using TeamCherry.Localization; using UnityEngine; [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: CompilationRelaxations(8)] [assembly: AssemblyVersion("0.0.0.0")] namespace CodexMods.CustomDifficulty; internal static class CustomDifficultyAdaptiveSystem { public const int SuggestOnly = 0; public const int ApplyWithConfirmation = 1; public const int ApplyAutomatically = 2; public static string GetApplyModeKey(int mode) { return mode switch { 1 => "adaptive_mode_confirm", 2 => "adaptive_mode_auto", _ => "adaptive_mode_suggest", }; } } internal sealed class CustomDifficultyBossProfileData { public string InternalName = string.Empty; public string DisplayName = string.Empty; public float HealthMultiplier = 1f; public float DamageMultiplier = 1f; public float HornetDamageMultiplier = 1f; public float SpeedMultiplier = 1f; public bool HealingAllowed = true; public bool Enabled; public string Notes = "Reserved until boss identification is safer."; } internal sealed class CustomDifficultyBossProfileManager { private readonly BaseUnityPlugin _plugin; private readonly string _profilePath; private List<CustomDifficultyBossProfileData> _cachedProfiles; private DateTime _cachedWriteTimeUtc; public string ProfilePath => _profilePath; public CustomDifficultyBossProfileManager(BaseUnityPlugin plugin) { _plugin = plugin; string text = Path.Combine(Paths.ConfigPath, "CustomDifficulty"); Directory.CreateDirectory(text); _profilePath = Path.Combine(text, "boss-profiles.json"); EnsureProfileFile(); } public List<CustomDifficultyBossProfileData> LoadProfiles() { try { DateTime dateTime = (File.Exists(_profilePath) ? File.GetLastWriteTimeUtc(_profilePath) : DateTime.MinValue); if (_cachedProfiles != null && dateTime == _cachedWriteTimeUtc) { return _cachedProfiles; } string text = File.ReadAllText(_profilePath); List<CustomDifficultyBossProfileData> list = JsonConvert.DeserializeObject<List<CustomDifficultyBossProfileData>>(text); _cachedProfiles = list ?? new List<CustomDifficultyBossProfileData>(); _cachedWriteTimeUtc = dateTime; return _cachedProfiles; } catch (Exception ex) { CustomDifficultyPlugin.LogWarningSafe("Could not load boss profiles: " + ex.Message); return new List<CustomDifficultyBossProfileData>(); } } public bool TryFindProfile(GameObject gameObject, out CustomDifficultyBossProfileData profile) { profile = null; if ((Object)(object)gameObject == (Object)null) { return false; } List<string> list = CollectObjectNames(gameObject); List<CustomDifficultyBossProfileData> list2 = LoadProfiles(); for (int i = 0; i < list2.Count; i++) { CustomDifficultyBossProfileData customDifficultyBossProfileData = list2[i]; if (customDifficultyBossProfileData == null || !customDifficultyBossProfileData.Enabled || string.IsNullOrWhiteSpace(customDifficultyBossProfileData.InternalName)) { continue; } string text = customDifficultyBossProfileData.InternalName.Trim(); if (string.Equals(text, "manual_boss_id", StringComparison.OrdinalIgnoreCase)) { continue; } for (int j = 0; j < list.Count; j++) { if (list[j].IndexOf(text, StringComparison.OrdinalIgnoreCase) >= 0) { profile = customDifficultyBossProfileData; return true; } } } return false; } private static List<string> CollectObjectNames(GameObject gameObject) { List<string> list = new List<string>(); try { Transform val = gameObject.transform; while ((Object)(object)val != (Object)null) { if (!string.IsNullOrEmpty(((Object)val).name)) { list.Add(((Object)val).name); } val = val.parent; } } catch { if (!string.IsNullOrEmpty(((Object)gameObject).name)) { list.Add(((Object)gameObject).name); } } try { MonoBehaviour[] componentsInParent = gameObject.GetComponentsInParent<MonoBehaviour>(true); foreach (MonoBehaviour val2 in componentsInParent) { if ((Object)(object)val2 != (Object)null) { list.Add(((object)val2).GetType().Name); list.Add(((object)val2).GetType().FullName); } } } catch { } return list; } private void EnsureProfileFile() { if (File.Exists(_profilePath)) { return; } List<CustomDifficultyBossProfileData> list = new List<CustomDifficultyBossProfileData>(); list.Add(new CustomDifficultyBossProfileData { InternalName = "lace", DisplayName = "Lace", HealthMultiplier = 1.2f, DamageMultiplier = 1.4f, SpeedMultiplier = 1.1f, Enabled = false }); list.Add(new CustomDifficultyBossProfileData { InternalName = "bell_beast", DisplayName = "Bell Beast", HealthMultiplier = 1.6f, DamageMultiplier = 1.2f, HealingAllowed = false, Enabled = false }); list.Add(new CustomDifficultyBossProfileData { InternalName = "manual_boss_id", DisplayName = "Manual Boss Profile", Enabled = false }); List<CustomDifficultyBossProfileData> list2 = list; try { File.WriteAllText(_profilePath, JsonConvert.SerializeObject((object)list2, (Formatting)1)); } catch (Exception ex) { CustomDifficultyPlugin.LogWarningSafe("Could not create boss profile file: " + ex.Message); } } } internal sealed class CustomDifficultyConfigManager { private readonly BaseUnityPlugin _plugin; private readonly ConfigEntry<KeyCode> _openMenuKey; private readonly ConfigEntry<int> _baseDamage; private readonly ConfigEntry<int> _baseHealth; private readonly ConfigEntry<float> _dropRate; private readonly ConfigEntry<float> _damageTakenMultiplier; private readonly ConfigEntry<float> _healingMultiplier; private readonly ConfigEntry<float> _healingCostMultiplier; private readonly ConfigEntry<float> _invincibilityTimeMultiplier; private readonly ConfigEntry<float> _skillDamageMultiplier; private readonly ConfigEntry<float> _silkSpearDamageMultiplier; private readonly ConfigEntry<float> _bossDamageMultiplier; private readonly ConfigEntry<float> _commonEnemyDamageMultiplier; private readonly ConfigEntry<float> _criticalChance; private readonly ConfigEntry<float> _criticalDamageMultiplier; private readonly ConfigEntry<float> _environmentalDamageMultiplier; private readonly ConfigEntry<float> _bossDamageTakenMultiplier; private readonly ConfigEntry<float> _commonEnemyDamageTakenMultiplier; private readonly ConfigEntry<float> _enemyHealthMultiplier; private readonly ConfigEntry<float> _enemyDamageMultiplier; private readonly ConfigEntry<float> _enemySpeedMultiplier; private readonly ConfigEntry<float> _enemyAttackCooldownMultiplier; private readonly ConfigEntry<float> _enemyAggressionMultiplier; private readonly ConfigEntry<float> _bossHealthMultiplier; private readonly ConfigEntry<float> _bossSpeedMultiplier; private readonly ConfigEntry<float> _bossAttackCooldownMultiplier; private readonly ConfigEntry<bool> _bossBrutalMode; private readonly ConfigEntry<bool> _disableHealingDuringBoss; private readonly ConfigEntry<float> _healingDuringBossMultiplier; private readonly ConfigEntry<float> _currencyDropMultiplier; private readonly ConfigEntry<float> _itemDropMultiplier; private readonly ConfigEntry<float> _shopPriceMultiplier; private readonly ConfigEntry<float> _bossRewardMultiplier; private readonly ConfigEntry<float> _deathCurrencyLossMultiplier; private readonly ConfigEntry<float> _playerSpeedMultiplier; private readonly ConfigEntry<float> _jumpHeightMultiplier; private readonly ConfigEntry<float> _dashCooldownMultiplier; private readonly ConfigEntry<float> _abilityCostMultiplier; private readonly ConfigEntry<float> _silkCostMultiplier; private readonly ConfigEntry<bool> _oneHitMode; private readonly ConfigEntry<bool> _mutatorNoHealing; private readonly ConfigEntry<bool> _mutatorLowGravity; private readonly ConfigEntry<bool> _mutatorDoubleDamage; private readonly ConfigEntry<bool> _mutatorGlassCannon; private readonly ConfigEntry<bool> _mutatorFastEnemies; private readonly ConfigEntry<bool> _mutatorTankEnemies; private readonly ConfigEntry<bool> _mutatorAggressiveEnemies; private readonly ConfigEntry<bool> _mutatorRandomizedEnemyHealth; private readonly ConfigEntry<bool> _mutatorRichWorld; private readonly ConfigEntry<bool> _mutatorPoorWorld; private readonly ConfigEntry<bool> _mutatorExpensiveShops; private readonly ConfigEntry<bool> _mutatorNoDeathLoss; private readonly ConfigEntry<bool> _mutatorBossFrenzy; private readonly ConfigEntry<bool> _mutatorNoMercy; private readonly ConfigEntry<bool> _mutatorExtendedFights; private readonly ConfigEntry<bool> _bossProfilesEnabled; private readonly ConfigEntry<bool> _bossProfilesFallbackGlobal; private readonly ConfigEntry<bool> _contextBossRulesEnabled; private readonly ConfigEntry<bool> _contextExplorationRulesEnabled; private readonly ConfigEntry<bool> _contextLowHealthRuleEnabled; private readonly ConfigEntry<bool> _contextDeathStreakTrackingEnabled; private readonly ConfigEntry<bool> _adaptiveDifficultyEnabled; private readonly ConfigEntry<bool> _adaptiveBossAssistEnabled; private readonly ConfigEntry<bool> _adaptiveExplorationAssistEnabled; private readonly ConfigEntry<bool> _adaptiveEconomyAssistEnabled; private readonly ConfigEntry<float> _adaptiveMaxAssist; private readonly ConfigEntry<int> _adaptiveApplyMode; private DifficultySettings _appliedSettings; private DifficultySettings _draftSettings; private int _settingsRevision; public int SettingsRevision => _settingsRevision; public CustomDifficultyConfigManager(BaseUnityPlugin plugin) { _plugin = plugin; _openMenuKey = plugin.Config.Bind<KeyCode>("Menu", "OpenMenuKey", (KeyCode)289, "Open the Custom Difficulty menu."); _baseDamage = plugin.Config.Bind<int>("Gameplay", "BaseDamage", 5, "Base character damage."); _baseHealth = plugin.Config.Bind<int>("Gameplay", "BaseHealth", 5, "Base maximum health."); _dropRate = plugin.Config.Bind<float>("Gameplay", "DropRate", 1f, "Global drop rate multiplier."); _damageTakenMultiplier = plugin.Config.Bind<float>("Gameplay", "DamageTakenMultiplier", 1f, "Global incoming damage multiplier."); _healingMultiplier = plugin.Config.Bind<float>("Player", "HealingMultiplier", 1f, "Healing amount multiplier."); _healingCostMultiplier = plugin.Config.Bind<float>("Player", "HealingCostMultiplier", 1f, "Healing silk cost multiplier."); _invincibilityTimeMultiplier = plugin.Config.Bind<float>("Player", "InvincibilityTimeMultiplier", 1f, "Invincibility time after damage."); _skillDamageMultiplier = plugin.Config.Bind<float>("Damage", "SkillDamageMultiplier", 1f, "Generic skill damage multiplier."); _silkSpearDamageMultiplier = plugin.Config.Bind<float>("Damage", "SilkSpearDamageMultiplier", 1f, "Silk spear damage multiplier."); _bossDamageMultiplier = plugin.Config.Bind<float>("Damage", "BossDamageMultiplier", 1f, "Damage dealt against bosses."); _commonEnemyDamageMultiplier = plugin.Config.Bind<float>("Damage", "CommonEnemyDamageMultiplier", 1f, "Damage dealt against common enemies."); _criticalChance = plugin.Config.Bind<float>("Damage", "CriticalChance", 0f, "Critical hit chance percentage."); _criticalDamageMultiplier = plugin.Config.Bind<float>("Damage", "CriticalDamageMultiplier", 2f, "Critical hit damage multiplier."); _environmentalDamageMultiplier = plugin.Config.Bind<float>("IncomingDamage", "EnvironmentalDamageMultiplier", 1f, "Environmental damage multiplier."); _bossDamageTakenMultiplier = plugin.Config.Bind<float>("IncomingDamage", "BossDamageTakenMultiplier", 1f, "Boss damage taken multiplier."); _commonEnemyDamageTakenMultiplier = plugin.Config.Bind<float>("IncomingDamage", "CommonEnemyDamageTakenMultiplier", 1f, "Common enemy damage taken multiplier."); _enemyHealthMultiplier = plugin.Config.Bind<float>("Enemies", "EnemyHealthMultiplier", 1f, "Common enemy health multiplier."); _enemyDamageMultiplier = plugin.Config.Bind<float>("Enemies", "EnemyDamageMultiplier", 1f, "Common enemy damage multiplier."); _enemySpeedMultiplier = plugin.Config.Bind<float>("Enemies", "EnemySpeedMultiplier", 1f, "Reserved enemy speed multiplier."); _enemyAttackCooldownMultiplier = plugin.Config.Bind<float>("Enemies", "EnemyAttackCooldownMultiplier", 1f, "Reserved enemy attack cooldown multiplier."); _enemyAggressionMultiplier = plugin.Config.Bind<float>("Enemies", "EnemyAggressionMultiplier", 1f, "Reserved enemy aggression multiplier."); _bossHealthMultiplier = plugin.Config.Bind<float>("Bosses", "BossHealthMultiplier", 1f, "Boss health multiplier."); _bossSpeedMultiplier = plugin.Config.Bind<float>("Bosses", "BossSpeedMultiplier", 1f, "Reserved boss speed multiplier."); _bossAttackCooldownMultiplier = plugin.Config.Bind<float>("Bosses", "BossAttackCooldownMultiplier", 1f, "Reserved boss attack cooldown multiplier."); _bossBrutalMode = plugin.Config.Bind<bool>("Bosses", "BossBrutalMode", false, "Enable brutal boss adjustments."); _disableHealingDuringBoss = plugin.Config.Bind<bool>("Bosses", "DisableHealingDuringBoss", false, "Reduce healing to zero during bosses."); _healingDuringBossMultiplier = plugin.Config.Bind<float>("Bosses", "HealingDuringBossMultiplier", 1f, "Healing multiplier during boss fights."); _currencyDropMultiplier = plugin.Config.Bind<float>("Drops", "CurrencyDropMultiplier", 1f, "Currency drop multiplier."); _itemDropMultiplier = plugin.Config.Bind<float>("Drops", "ItemDropMultiplier", 1f, "Item drop multiplier."); _shopPriceMultiplier = plugin.Config.Bind<float>("Economy", "ShopPriceMultiplier", 1f, "Experimental shop price multiplier."); _bossRewardMultiplier = plugin.Config.Bind<float>("Economy", "BossRewardMultiplier", 1f, "Experimental boss reward multiplier."); _deathCurrencyLossMultiplier = plugin.Config.Bind<float>("Economy", "DeathCurrencyLossMultiplier", 1f, "Reserved death currency loss multiplier."); _playerSpeedMultiplier = plugin.Config.Bind<float>("Movement", "PlayerSpeedMultiplier", 1f, "Player speed multiplier."); _jumpHeightMultiplier = plugin.Config.Bind<float>("Movement", "JumpHeightMultiplier", 1f, "Jump height multiplier."); _dashCooldownMultiplier = plugin.Config.Bind<float>("Movement", "DashCooldownMultiplier", 1f, "Dash cooldown multiplier."); _abilityCostMultiplier = plugin.Config.Bind<float>("Movement", "AbilityCostMultiplier", 1f, "Ability cost multiplier."); _silkCostMultiplier = plugin.Config.Bind<float>("Movement", "SilkCostMultiplier", 1f, "Silk ability cost multiplier."); _oneHitMode = plugin.Config.Bind<bool>("System", "OneHitMode", false, "Enable one hit mode."); _mutatorNoHealing = plugin.Config.Bind<bool>("Mutators", "NoHealing", false, "Disable normal healing."); _mutatorLowGravity = plugin.Config.Bind<bool>("Mutators", "LowGravity", false, "Slightly increases jump feel through the existing jump hook."); _mutatorDoubleDamage = plugin.Config.Bind<bool>("Mutators", "DoubleDamage", false, "Double Hornet outgoing damage."); _mutatorGlassCannon = plugin.Config.Bind<bool>("Mutators", "GlassCannon", false, "Large outgoing damage with higher incoming damage."); _mutatorFastEnemies = plugin.Config.Bind<bool>("Mutators", "FastEnemies", false, "Experimental enemy speed mutator."); _mutatorTankEnemies = plugin.Config.Bind<bool>("Mutators", "TankEnemies", false, "Common enemies effectively take less damage."); _mutatorAggressiveEnemies = plugin.Config.Bind<bool>("Mutators", "AggressiveEnemies", false, "Experimental enemy pressure mutator."); _mutatorRandomizedEnemyHealth = plugin.Config.Bind<bool>("Mutators", "RandomizedEnemyHealth", false, "Experimental randomized enemy effective health."); _mutatorRichWorld = plugin.Config.Bind<bool>("Mutators", "RichWorld", false, "Increase world drops."); _mutatorPoorWorld = plugin.Config.Bind<bool>("Mutators", "PoorWorld", false, "Reduce world drops."); _mutatorExpensiveShops = plugin.Config.Bind<bool>("Mutators", "ExpensiveShops", false, "Experimental shop price mutator."); _mutatorNoDeathLoss = plugin.Config.Bind<bool>("Mutators", "NoDeathLoss", false, "Reserved death-loss mutator stored for future hooks."); _mutatorBossFrenzy = plugin.Config.Bind<bool>("Mutators", "BossFrenzy", false, "Boss fights receive extra pressure through safe existing hooks."); _mutatorNoMercy = plugin.Config.Bind<bool>("Mutators", "NoMercy", false, "Disable healing during boss fights."); _mutatorExtendedFights = plugin.Config.Bind<bool>("Mutators", "ExtendedFights", false, "Bosses effectively take less damage."); _bossProfilesEnabled = plugin.Config.Bind<bool>("BossProfiles", "Enabled", false, "Experimental boss profile matching by object or component name."); _bossProfilesFallbackGlobal = plugin.Config.Bind<bool>("BossProfiles", "FallbackGlobal", true, "Use global boss settings when a boss profile is not identified."); _contextBossRulesEnabled = plugin.Config.Bind<bool>("ContextRules", "BossRulesEnabled", false, "Reserved context rule for boss fights."); _contextExplorationRulesEnabled = plugin.Config.Bind<bool>("ContextRules", "ExplorationRulesEnabled", false, "Reserved context rule for exploration."); _contextLowHealthRuleEnabled = plugin.Config.Bind<bool>("ContextRules", "LowHealthRuleEnabled", false, "Reserved context rule for low health."); _contextDeathStreakTrackingEnabled = plugin.Config.Bind<bool>("ContextRules", "DeathStreakTrackingEnabled", false, "Reserved context rule for repeated deaths."); _adaptiveDifficultyEnabled = plugin.Config.Bind<bool>("Adaptive", "Enabled", false, "Enable adaptive difficulty structure. Default is off."); _adaptiveBossAssistEnabled = plugin.Config.Bind<bool>("Adaptive", "BossAssist", false, "Reserved adaptive boss assistance."); _adaptiveExplorationAssistEnabled = plugin.Config.Bind<bool>("Adaptive", "ExplorationAssist", false, "Reserved adaptive exploration assistance."); _adaptiveEconomyAssistEnabled = plugin.Config.Bind<bool>("Adaptive", "EconomyAssist", false, "Reserved adaptive economy assistance."); _adaptiveMaxAssist = plugin.Config.Bind<float>("Adaptive", "MaxAssist", 0.25f, "Maximum adaptive assistance from 0.0 to 1.0."); _adaptiveApplyMode = plugin.Config.Bind<int>("Adaptive", "ApplyMode", 0, "0=suggest only, 1=confirm, 2=automatic."); _appliedSettings = ReadSettingsFromConfig(); _draftSettings = _appliedSettings.Clone(); } public KeyCode GetOpenMenuKey() { //IL_0006: Unknown result type (might be due to invalid IL or missing references) return _openMenuKey.Value; } public DifficultySettings GetAppliedSettings() { return _appliedSettings; } public DifficultySettings GetDraftSettings() { return _draftSettings; } public bool HasUnsavedChanges() { return !_draftSettings.IsEquivalent(_appliedSettings); } public void ResetDraftToApplied() { _draftSettings = _appliedSettings.Clone(); } public void ResetDraftToDefaults() { _draftSettings = DifficultySettings.CreateDefaults(); } public void SetDraftSettings(DifficultySettings settings) { _draftSettings = ((settings != null) ? settings.Clone() : DifficultySettings.CreateDefaults()); _draftSettings.Clamp(); } public void ApplyPresetToDraft(DifficultyPreset preset) { if (preset != DifficultyPreset.Custom) { _draftSettings.ApplyPreset(preset); } } public void ApplyDraft() { _draftSettings.Clamp(); _baseDamage.Value = _draftSettings.BaseDamage; _baseHealth.Value = _draftSettings.BaseHealth; _dropRate.Value = _draftSettings.DropRate; _damageTakenMultiplier.Value = _draftSettings.DamageTakenMultiplier; _healingMultiplier.Value = _draftSettings.HealingMultiplier; _healingCostMultiplier.Value = _draftSettings.HealingCostMultiplier; _invincibilityTimeMultiplier.Value = _draftSettings.InvincibilityTimeMultiplier; _skillDamageMultiplier.Value = _draftSettings.SkillDamageMultiplier; _silkSpearDamageMultiplier.Value = _draftSettings.SilkSpearDamageMultiplier; _bossDamageMultiplier.Value = _draftSettings.BossDamageMultiplier; _commonEnemyDamageMultiplier.Value = _draftSettings.CommonEnemyDamageMultiplier; _criticalChance.Value = _draftSettings.CriticalChance; _criticalDamageMultiplier.Value = _draftSettings.CriticalDamageMultiplier; _environmentalDamageMultiplier.Value = _draftSettings.EnvironmentalDamageMultiplier; _bossDamageTakenMultiplier.Value = _draftSettings.BossDamageTakenMultiplier; _commonEnemyDamageTakenMultiplier.Value = _draftSettings.CommonEnemyDamageTakenMultiplier; _enemyHealthMultiplier.Value = _draftSettings.EnemyHealthMultiplier; _enemyDamageMultiplier.Value = _draftSettings.EnemyDamageMultiplier; _enemySpeedMultiplier.Value = _draftSettings.EnemySpeedMultiplier; _enemyAttackCooldownMultiplier.Value = _draftSettings.EnemyAttackCooldownMultiplier; _enemyAggressionMultiplier.Value = _draftSettings.EnemyAggressionMultiplier; _bossHealthMultiplier.Value = _draftSettings.BossHealthMultiplier; _bossSpeedMultiplier.Value = _draftSettings.BossSpeedMultiplier; _bossAttackCooldownMultiplier.Value = _draftSettings.BossAttackCooldownMultiplier; _bossBrutalMode.Value = _draftSettings.BossBrutalMode; _disableHealingDuringBoss.Value = _draftSettings.DisableHealingDuringBoss; _healingDuringBossMultiplier.Value = _draftSettings.HealingDuringBossMultiplier; _currencyDropMultiplier.Value = _draftSettings.CurrencyDropMultiplier; _itemDropMultiplier.Value = _draftSettings.ItemDropMultiplier; _shopPriceMultiplier.Value = _draftSettings.ShopPriceMultiplier; _bossRewardMultiplier.Value = _draftSettings.BossRewardMultiplier; _deathCurrencyLossMultiplier.Value = _draftSettings.DeathCurrencyLossMultiplier; _playerSpeedMultiplier.Value = _draftSettings.PlayerSpeedMultiplier; _jumpHeightMultiplier.Value = _draftSettings.JumpHeightMultiplier; _dashCooldownMultiplier.Value = _draftSettings.DashCooldownMultiplier; _abilityCostMultiplier.Value = _draftSettings.AbilityCostMultiplier; _silkCostMultiplier.Value = _draftSettings.SilkCostMultiplier; _oneHitMode.Value = _draftSettings.OneHitMode; _mutatorNoHealing.Value = _draftSettings.MutatorNoHealing; _mutatorLowGravity.Value = _draftSettings.MutatorLowGravity; _mutatorDoubleDamage.Value = _draftSettings.MutatorDoubleDamage; _mutatorGlassCannon.Value = _draftSettings.MutatorGlassCannon; _mutatorFastEnemies.Value = _draftSettings.MutatorFastEnemies; _mutatorTankEnemies.Value = _draftSettings.MutatorTankEnemies; _mutatorAggressiveEnemies.Value = _draftSettings.MutatorAggressiveEnemies; _mutatorRandomizedEnemyHealth.Value = _draftSettings.MutatorRandomizedEnemyHealth; _mutatorRichWorld.Value = _draftSettings.MutatorRichWorld; _mutatorPoorWorld.Value = _draftSettings.MutatorPoorWorld; _mutatorExpensiveShops.Value = _draftSettings.MutatorExpensiveShops; _mutatorNoDeathLoss.Value = _draftSettings.MutatorNoDeathLoss; _mutatorBossFrenzy.Value = _draftSettings.MutatorBossFrenzy; _mutatorNoMercy.Value = _draftSettings.MutatorNoMercy; _mutatorExtendedFights.Value = _draftSettings.MutatorExtendedFights; _bossProfilesEnabled.Value = _draftSettings.BossProfilesEnabled; _bossProfilesFallbackGlobal.Value = _draftSettings.BossProfilesFallbackGlobal; _contextBossRulesEnabled.Value = _draftSettings.ContextBossRulesEnabled; _contextExplorationRulesEnabled.Value = _draftSettings.ContextExplorationRulesEnabled; _contextLowHealthRuleEnabled.Value = _draftSettings.ContextLowHealthRuleEnabled; _contextDeathStreakTrackingEnabled.Value = _draftSettings.ContextDeathStreakTrackingEnabled; _adaptiveDifficultyEnabled.Value = _draftSettings.AdaptiveDifficultyEnabled; _adaptiveBossAssistEnabled.Value = _draftSettings.AdaptiveBossAssistEnabled; _adaptiveExplorationAssistEnabled.Value = _draftSettings.AdaptiveExplorationAssistEnabled; _adaptiveEconomyAssistEnabled.Value = _draftSettings.AdaptiveEconomyAssistEnabled; _adaptiveMaxAssist.Value = _draftSettings.AdaptiveMaxAssist; _adaptiveApplyMode.Value = _draftSettings.AdaptiveApplyMode; _plugin.Config.Save(); _appliedSettings = _draftSettings.Clone(); _settingsRevision++; } private DifficultySettings ReadSettingsFromConfig() { DifficultySettings difficultySettings = DifficultySettings.CreateDefaults(); difficultySettings.BaseDamage = _baseDamage.Value; difficultySettings.BaseHealth = _baseHealth.Value; difficultySettings.DropRate = _dropRate.Value; difficultySettings.DamageTakenMultiplier = _damageTakenMultiplier.Value; difficultySettings.HealingMultiplier = _healingMultiplier.Value; difficultySettings.HealingCostMultiplier = _healingCostMultiplier.Value; difficultySettings.InvincibilityTimeMultiplier = _invincibilityTimeMultiplier.Value; difficultySettings.SkillDamageMultiplier = _skillDamageMultiplier.Value; difficultySettings.SilkSpearDamageMultiplier = _silkSpearDamageMultiplier.Value; difficultySettings.BossDamageMultiplier = _bossDamageMultiplier.Value; difficultySettings.CommonEnemyDamageMultiplier = _commonEnemyDamageMultiplier.Value; difficultySettings.CriticalChance = _criticalChance.Value; difficultySettings.CriticalDamageMultiplier = _criticalDamageMultiplier.Value; difficultySettings.EnvironmentalDamageMultiplier = _environmentalDamageMultiplier.Value; difficultySettings.BossDamageTakenMultiplier = _bossDamageTakenMultiplier.Value; difficultySettings.CommonEnemyDamageTakenMultiplier = _commonEnemyDamageTakenMultiplier.Value; difficultySettings.EnemyHealthMultiplier = _enemyHealthMultiplier.Value; difficultySettings.EnemyDamageMultiplier = _enemyDamageMultiplier.Value; difficultySettings.EnemySpeedMultiplier = _enemySpeedMultiplier.Value; difficultySettings.EnemyAttackCooldownMultiplier = _enemyAttackCooldownMultiplier.Value; difficultySettings.EnemyAggressionMultiplier = _enemyAggressionMultiplier.Value; difficultySettings.BossHealthMultiplier = _bossHealthMultiplier.Value; difficultySettings.BossSpeedMultiplier = _bossSpeedMultiplier.Value; difficultySettings.BossAttackCooldownMultiplier = _bossAttackCooldownMultiplier.Value; difficultySettings.BossBrutalMode = _bossBrutalMode.Value; difficultySettings.DisableHealingDuringBoss = _disableHealingDuringBoss.Value; difficultySettings.HealingDuringBossMultiplier = _healingDuringBossMultiplier.Value; difficultySettings.CurrencyDropMultiplier = _currencyDropMultiplier.Value; difficultySettings.ItemDropMultiplier = _itemDropMultiplier.Value; difficultySettings.ShopPriceMultiplier = _shopPriceMultiplier.Value; difficultySettings.BossRewardMultiplier = _bossRewardMultiplier.Value; difficultySettings.DeathCurrencyLossMultiplier = _deathCurrencyLossMultiplier.Value; difficultySettings.PlayerSpeedMultiplier = _playerSpeedMultiplier.Value; difficultySettings.JumpHeightMultiplier = _jumpHeightMultiplier.Value; difficultySettings.DashCooldownMultiplier = _dashCooldownMultiplier.Value; difficultySettings.AbilityCostMultiplier = _abilityCostMultiplier.Value; difficultySettings.SilkCostMultiplier = _silkCostMultiplier.Value; difficultySettings.OneHitMode = _oneHitMode.Value; difficultySettings.MutatorNoHealing = _mutatorNoHealing.Value; difficultySettings.MutatorLowGravity = _mutatorLowGravity.Value; difficultySettings.MutatorDoubleDamage = _mutatorDoubleDamage.Value; difficultySettings.MutatorGlassCannon = _mutatorGlassCannon.Value; difficultySettings.MutatorFastEnemies = _mutatorFastEnemies.Value; difficultySettings.MutatorTankEnemies = _mutatorTankEnemies.Value; difficultySettings.MutatorAggressiveEnemies = _mutatorAggressiveEnemies.Value; difficultySettings.MutatorRandomizedEnemyHealth = _mutatorRandomizedEnemyHealth.Value; difficultySettings.MutatorRichWorld = _mutatorRichWorld.Value; difficultySettings.MutatorPoorWorld = _mutatorPoorWorld.Value; difficultySettings.MutatorExpensiveShops = _mutatorExpensiveShops.Value; difficultySettings.MutatorNoDeathLoss = _mutatorNoDeathLoss.Value; difficultySettings.MutatorBossFrenzy = _mutatorBossFrenzy.Value; difficultySettings.MutatorNoMercy = _mutatorNoMercy.Value; difficultySettings.MutatorExtendedFights = _mutatorExtendedFights.Value; difficultySettings.BossProfilesEnabled = _bossProfilesEnabled.Value; difficultySettings.BossProfilesFallbackGlobal = _bossProfilesFallbackGlobal.Value; difficultySettings.ContextBossRulesEnabled = _contextBossRulesEnabled.Value; difficultySettings.ContextExplorationRulesEnabled = _contextExplorationRulesEnabled.Value; difficultySettings.ContextLowHealthRuleEnabled = _contextLowHealthRuleEnabled.Value; difficultySettings.ContextDeathStreakTrackingEnabled = _contextDeathStreakTrackingEnabled.Value; difficultySettings.AdaptiveDifficultyEnabled = _adaptiveDifficultyEnabled.Value; difficultySettings.AdaptiveBossAssistEnabled = _adaptiveBossAssistEnabled.Value; difficultySettings.AdaptiveExplorationAssistEnabled = _adaptiveExplorationAssistEnabled.Value; difficultySettings.AdaptiveEconomyAssistEnabled = _adaptiveEconomyAssistEnabled.Value; difficultySettings.AdaptiveMaxAssist = _adaptiveMaxAssist.Value; difficultySettings.AdaptiveApplyMode = _adaptiveApplyMode.Value; difficultySettings.Clamp(); return difficultySettings; } } internal static class CustomDifficultyContextRules { public static bool HasAnyRuleEnabled(DifficultySettings settings) { if (settings == null) { return false; } if (!settings.ContextBossRulesEnabled && !settings.ContextExplorationRulesEnabled && !settings.ContextLowHealthRuleEnabled) { return settings.ContextDeathStreakTrackingEnabled; } return true; } } internal sealed class DifficultySummary { public string RatingKey; public float Score; public readonly List<string> Lines = new List<string>(); } internal static class CustomDifficultyDifficultySummary { public static DifficultySummary Build(DifficultySettings settings, CustomDifficultyLocalizationManager localization) { DifficultySettings difficultySettings = DifficultySettings.CreateDefaults(); DifficultySummary difficultySummary = new DifficultySummary(); float num = 0f; num += PercentDelta(settings.DamageTakenMultiplier, difficultySettings.DamageTakenMultiplier) * 1.6f; num += PercentDelta(settings.BossDamageTakenMultiplier, difficultySettings.BossDamageTakenMultiplier) * 1.2f; num += PercentDelta(settings.CommonEnemyDamageTakenMultiplier, difficultySettings.CommonEnemyDamageTakenMultiplier) * 0.9f; num += PercentDelta(settings.EnemyDamageMultiplier, difficultySettings.EnemyDamageMultiplier) * 1f; num += PercentDelta(settings.EnemyHealthMultiplier, difficultySettings.EnemyHealthMultiplier) * 0.8f; num += PercentDelta(settings.BossHealthMultiplier, difficultySettings.BossHealthMultiplier) * 1.4f; num += PercentDelta(settings.ShopPriceMultiplier, difficultySettings.ShopPriceMultiplier) * 0.5f; num -= PercentDelta(settings.BaseHealth, difficultySettings.BaseHealth) * 1.5f; num -= PercentDelta(settings.BaseDamage, difficultySettings.BaseDamage) * 1.2f; num -= PercentDelta(settings.HealingMultiplier, difficultySettings.HealingMultiplier) * 1f; num += PercentDelta(settings.HealingCostMultiplier, difficultySettings.HealingCostMultiplier) * 0.8f; num -= PercentDelta(settings.DropRate, difficultySettings.DropRate) * 0.4f; num -= PercentDelta(settings.CurrencyDropMultiplier, difficultySettings.CurrencyDropMultiplier) * 0.3f; num -= PercentDelta(settings.ItemDropMultiplier, difficultySettings.ItemDropMultiplier) * 0.3f; if (settings.OneHitMode) { num += 120f; } if (settings.MutatorNoHealing) { num += 40f; } if (settings.MutatorGlassCannon) { num += 25f; } if (settings.MutatorBossFrenzy) { num += 30f; } if (settings.MutatorNoMercy) { num += 30f; } if (settings.MutatorExtendedFights || settings.MutatorTankEnemies) { num += 18f; } if (settings.MutatorRichWorld) { num -= 16f; } if (settings.MutatorPoorWorld) { num += 14f; } difficultySummary.Score = num; difficultySummary.RatingKey = GetRatingKey(num, settings); difficultySummary.Lines.Add(localization.Get("summary_player_health") + ": " + FormatIntDelta(settings.BaseHealth, difficultySettings.BaseHealth)); difficultySummary.Lines.Add(localization.Get("summary_damage_taken") + ": " + FormatMultiplierDelta(settings.DamageTakenMultiplier, difficultySettings.DamageTakenMultiplier)); difficultySummary.Lines.Add(localization.Get("summary_hornet_damage") + ": " + FormatIntDelta(settings.BaseDamage, difficultySettings.BaseDamage)); difficultySummary.Lines.Add(localization.Get("summary_bosses") + ": " + DescribeBosses(settings, localization)); difficultySummary.Lines.Add(localization.Get("summary_enemies") + ": " + DescribeEnemies(settings, localization)); difficultySummary.Lines.Add(localization.Get("summary_economy") + ": " + DescribeEconomy(settings, localization)); difficultySummary.Lines.Add(localization.Get("summary_healing") + ": " + DescribeHealing(settings, localization)); difficultySummary.Lines.Add(localization.Get("summary_drops") + ": " + FormatMultiplierDelta(settings.DropRate * settings.CurrencyDropMultiplier * CustomDifficultyMutatorManager.GetDropMultiplier(settings), 1f)); difficultySummary.Lines.Add(localization.Get("summary_mutators") + ": " + CustomDifficultyMutatorManager.CountEnabled(settings)); return difficultySummary; } private static string GetRatingKey(float score, DifficultySettings settings) { if (settings.OneHitMode || score >= 95f) { return "rating_chaotic"; } if (score >= 65f) { return "rating_extreme"; } if (score >= 28f) { return "rating_hard"; } if (score <= -45f) { return "rating_very_easy"; } if (score <= -18f) { return "rating_easy"; } return "rating_normal"; } private static string DescribeBosses(DifficultySettings settings, CustomDifficultyLocalizationManager localization) { float num = settings.BossHealthMultiplier * settings.BossDamageTakenMultiplier; if (settings.BossBrutalMode || settings.MutatorBossFrenzy || settings.MutatorExtendedFights) { num += 0.35f; } return DescribePressure(num, localization); } private static string DescribeEnemies(DifficultySettings settings, CustomDifficultyLocalizationManager localization) { float num = settings.EnemyHealthMultiplier * settings.EnemyDamageMultiplier * settings.CommonEnemyDamageTakenMultiplier; if (settings.MutatorTankEnemies || settings.MutatorAggressiveEnemies) { num += 0.25f; } return DescribePressure(num, localization); } private static string DescribeEconomy(DifficultySettings settings, CustomDifficultyLocalizationManager localization) { float num = settings.CurrencyDropMultiplier * settings.DropRate / Math.Max(0.1f, settings.ShopPriceMultiplier); num *= CustomDifficultyMutatorManager.GetDropMultiplier(settings); num /= Math.Max(0.1f, CustomDifficultyMutatorManager.GetShopPriceMultiplier(settings)); if (num >= 1.35f) { return localization.Get("summary_generous"); } if (num <= 0.75f) { return localization.Get("summary_strict"); } return localization.Get("summary_normal"); } private static string DescribeHealing(DifficultySettings settings, CustomDifficultyLocalizationManager localization) { if (settings.MutatorNoHealing || settings.OneHitMode) { return localization.Get("summary_blocked"); } float num = settings.HealingMultiplier / Math.Max(0.1f, settings.HealingCostMultiplier); if (settings.DisableHealingDuringBoss || settings.MutatorNoMercy) { num *= 0.65f; } if (num >= 1.25f) { return localization.Get("summary_generous"); } if (num <= 0.75f) { return localization.Get("summary_reduced"); } return localization.Get("summary_normal"); } private static string DescribePressure(float value, CustomDifficultyLocalizationManager localization) { if (value >= 1.75f) { return localization.Get("summary_much_harder"); } if (value >= 1.2f) { return localization.Get("summary_slightly_harder"); } if (value <= 0.8f) { return localization.Get("summary_easier"); } return localization.Get("summary_normal"); } private static string FormatMultiplierDelta(float current, float defaults) { float num = (current - defaults) * 100f; if (Math.Abs(num) < 0.1f) { return "x" + current.ToString("0.0") + " (default)"; } return "x" + current.ToString("0.0") + " (" + ((num > 0f) ? "+" : string.Empty) + num.ToString("0") + "%)"; } private static string FormatIntDelta(int current, int defaults) { int num = current - defaults; if (num == 0) { return current + " (default)"; } return current + " (" + ((num > 0) ? "+" : string.Empty) + num + ")"; } private static float PercentDelta(float current, float defaults) { if (Math.Abs(defaults) < 0.001f) { return current * 100f; } return (current - defaults) / defaults * 100f; } private static float PercentDelta(int current, int defaults) { if (defaults == 0) { return (float)current * 100f; } return (float)(current - defaults) / (float)defaults * 100f; } } internal sealed class CustomDifficultyFavorites { private readonly BaseUnityPlugin _plugin; private readonly ConfigEntry<string> _favoriteOptionIds; private readonly HashSet<string> _favorites = new HashSet<string>(StringComparer.OrdinalIgnoreCase); public int Count => _favorites.Count; public CustomDifficultyFavorites(BaseUnityPlugin plugin) { _plugin = plugin; _favoriteOptionIds = plugin.Config.Bind<string>("Menu", "FavoriteOptionIds", "[]", "JSON array with Custom Difficulty menu option ids marked as favorites."); Load(); } public bool IsFavorite(string optionId) { if (!string.IsNullOrEmpty(optionId)) { return _favorites.Contains(optionId); } return false; } public void Toggle(string optionId) { if (!string.IsNullOrEmpty(optionId)) { if (_favorites.Contains(optionId)) { _favorites.Remove(optionId); } else { _favorites.Add(optionId); } Save(); } } public List<string> GetAll() { return new List<string>(_favorites); } public void ReplaceAll(IEnumerable<string> optionIds) { _favorites.Clear(); if (optionIds != null) { foreach (string optionId in optionIds) { if (!string.IsNullOrEmpty(optionId)) { _favorites.Add(optionId); } } } Save(); } private void Load() { _favorites.Clear(); try { List<string> list = JsonConvert.DeserializeObject<List<string>>(_favoriteOptionIds.Value); if (list == null) { return; } foreach (string item in list) { if (!string.IsNullOrEmpty(item)) { _favorites.Add(item); } } } catch { _favoriteOptionIds.Value = "[]"; _plugin.Config.Save(); } } private void Save() { _favoriteOptionIds.Value = JsonConvert.SerializeObject((object)GetAll()); _plugin.Config.Save(); } } internal sealed class CustomDifficultyGameplayHooks { private sealed class HealthProfileStateFile { public Dictionary<string, int> AppliedBaseHealthByProfile = new Dictionary<string, int>(); public Dictionary<string, int> BonusHealthByProfile = new Dictionary<string, int>(); } private sealed class HeroRuntimeDefaults { public int HeroControllerId; public float RunSpeed; public float WalkSpeed; public float QuickeningRunSpeed; public float QuickeningWalkSpeed; public float JumpSpeed; public float MinJumpSpeed; public float DashCooldown; public float InvulTime; public float InvulTimeCrossStitch; public float InvulTimeParry; public float InvulTimeQuake; public float InvulTimeSilkDash; } private static readonly FieldInfo MaxHealthField = AccessTools.Field(typeof(PlayerData), "maxHealth"); private static readonly FieldInfo MaxHealthBaseField = AccessTools.Field(typeof(PlayerData), "maxHealthBase"); private static readonly FieldInfo HealthField = AccessTools.Field(typeof(PlayerData), "health"); private static readonly FieldInfo PrevHealthField = AccessTools.Field(typeof(PlayerData), "prevHealth"); private static readonly FieldInfo ProfileIdField = AccessTools.Field(typeof(PlayerData), "profileID"); private static readonly MethodInfo PlayerDataMaxHealthMethod = AccessTools.Method(typeof(PlayerData), "MaxHealth", (Type[])null, (Type[])null); private static readonly MethodInfo HeroControllerMaxHealthKeepBlueMethod = AccessTools.Method(typeof(HeroController), "MaxHealthKeepBlue", (Type[])null, (Type[])null); private static readonly FieldInfo HeroRunSpeedField = AccessTools.Field(typeof(HeroController), "RUN_SPEED"); private static readonly FieldInfo HeroWalkSpeedField = AccessTools.Field(typeof(HeroController), "WALK_SPEED"); private static readonly FieldInfo HeroQuickeningRunSpeedField = AccessTools.Field(typeof(HeroController), "QUICKENING_RUN_SPEED"); private static readonly FieldInfo HeroQuickeningWalkSpeedField = AccessTools.Field(typeof(HeroController), "QUICKENING_WALK_SPEED"); private static readonly FieldInfo HeroJumpSpeedField = AccessTools.Field(typeof(HeroController), "JUMP_SPEED"); private static readonly FieldInfo HeroMinJumpSpeedField = AccessTools.Field(typeof(HeroController), "MIN_JUMP_SPEED"); private static readonly FieldInfo HeroDashCooldownField = AccessTools.Field(typeof(HeroController), "DASH_COOLDOWN"); private static readonly FieldInfo HeroDashCooldownTimerField = AccessTools.Field(typeof(HeroController), "dashCooldownTimer"); private static readonly FieldInfo HeroInvulTimeField = AccessTools.Field(typeof(HeroController), "INVUL_TIME"); private static readonly FieldInfo HeroInvulTimeCrossStitchField = AccessTools.Field(typeof(HeroController), "INVUL_TIME_CROSS_STITCH"); private static readonly FieldInfo HeroInvulTimeParryField = AccessTools.Field(typeof(HeroController), "INVUL_TIME_PARRY"); private static readonly FieldInfo HeroInvulTimeQuakeField = AccessTools.Field(typeof(HeroController), "INVUL_TIME_QUAKE"); private static readonly FieldInfo HeroInvulTimeSilkDashField = AccessTools.Field(typeof(HeroController), "INVUL_TIME_SILKDASH"); private static readonly FieldInfo HeroHarpoonDashCooldownField = AccessTools.Field(typeof(HeroController), "harpoonDashCooldown"); private static readonly FieldInfo DamageEnemiesAttackTypeField = AccessTools.Field(typeof(DamageEnemies), "attackType"); private static readonly FieldInfo DamageEnemiesRepresentingToolField = AccessTools.Field(typeof(DamageEnemies), "representingTool"); private static readonly FieldInfo DamageEnemiesUseNailDamageField = AccessTools.Field(typeof(DamageEnemies), "useNailDamage"); private static readonly FieldInfo DamageEnemiesIsNailAttackField = AccessTools.Field(typeof(DamageEnemies), "isNailAttack"); private static readonly FieldInfo DamageEnemiesDoesNotCriticalHitField = AccessTools.Field(typeof(DamageEnemies), "doesNotCriticalHit"); private static readonly FieldInfo DamageEnemiesSpecialTypeField = AccessTools.Field(typeof(DamageEnemies), "specialType"); private static readonly Dictionary<int, Stack<GameObject>> DamageTargetContexts = new Dictionary<int, Stack<GameObject>>(); [ThreadStatic] private static bool _skipFallbackIncomingDamageScaling; private readonly BaseUnityPlugin _plugin; private readonly CustomDifficultyConfigManager _configManager; private readonly CustomDifficultyBossProfileManager _bossProfileManager; private readonly Dictionary<int, int> _appliedBaseHealthByProfile = new Dictionary<int, int>(); private readonly Dictionary<int, int> _bonusHealthByProfile = new Dictionary<int, int>(); private readonly Dictionary<int, float> _randomEnemyHealthByTarget = new Dictionary<int, float>(); private readonly HashSet<string> _reportedBossProfiles = new HashSet<string>(StringComparer.OrdinalIgnoreCase); private readonly HashSet<int> _trackedHealthProfiles = new HashSet<int>(); private readonly string _stateFilePath; private HeroRuntimeDefaults _heroRuntimeDefaults; private int _lastProfileId = -1; private int _lastSettingsRevision = -1; private int _lastHeroControllerId = -1; private float _lastSyncAt = -99f; private bool _healthUiSynced; private float _lastBossSceneRefreshAt = -99f; private bool _cachedBossSceneActive; public static CustomDifficultyGameplayHooks Instance; public CustomDifficultyGameplayHooks(BaseUnityPlugin plugin, CustomDifficultyConfigManager configManager, CustomDifficultyBossProfileManager bossProfileManager) { _plugin = plugin; _configManager = configManager; _bossProfileManager = bossProfileManager; _stateFilePath = Path.Combine(Paths.ConfigPath, "CustomDifficulty", "profile-health-state.json"); LoadProfileState(); Instance = this; } public void Update() { ApplyHeroRuntimeTuning(); if (PlayerData.HasInstance) { PlayerData instance = PlayerData.instance; int profileId = GetProfileId(instance); int num = (((Object)(object)HeroController.instance != (Object)null) ? ((Object)HeroController.instance).GetInstanceID() : (-1)); bool flag = false; if (profileId != _lastProfileId) { _lastProfileId = profileId; _healthUiSynced = false; flag = true; } if (_lastSettingsRevision != _configManager.SettingsRevision) { _lastSettingsRevision = _configManager.SettingsRevision; _healthUiSynced = false; flag = true; } if (num != _lastHeroControllerId) { _lastHeroControllerId = num; _healthUiSynced = false; _heroRuntimeDefaults = null; flag = true; } if (Time.unscaledTime - _lastSyncAt > 1f) { flag = true; } if (flag) { SyncBaseHealth(instance, force: false); } } } public void ApplyImmediateChanges() { _lastSettingsRevision = _configManager.SettingsRevision; _lastSyncAt = -99f; _lastBossSceneRefreshAt = -99f; _randomEnemyHealthByTarget.Clear(); _reportedBossProfiles.Clear(); if (PlayerData.HasInstance) { SyncBaseHealth(PlayerData.instance, force: true); } ApplyHeroRuntimeTuning(); } public void RegisterPermanentHealthUpgrade(int amount) { if (amount > 0 && PlayerData.HasInstance) { int profileId = GetProfileId(PlayerData.instance); if (_trackedHealthProfiles.Contains(profileId)) { int value = 0; _bonusHealthByProfile.TryGetValue(profileId, out value); _bonusHealthByProfile[profileId] = Math.Max(0, value + amount); SaveProfileState(); } } } public int GetScaledBaseDamage(int vanillaDamage) { DifficultySettings appliedSettings = _configManager.GetAppliedSettings(); int num = 0; if (vanillaDamage > 5) { num = Math.Max(0, (vanillaDamage - 5) / 4); } int val = appliedSettings.BaseDamage + num * 4; return Math.Max(1, Math.Min(99999, val)); } public int ScaleHealingAmount(int amount) { if (amount <= 0) { return amount; } DifficultySettings appliedSettings = _configManager.GetAppliedSettings(); bool flag = IsInBossScene(); if (CustomDifficultyMutatorManager.BlocksHealing(appliedSettings, flag)) { return 0; } float num = appliedSettings.HealingMultiplier; if (flag) { if (appliedSettings.DisableHealingDuringBoss) { return 0; } num *= appliedSettings.HealingDuringBossMultiplier; if (appliedSettings.BossBrutalMode) { num *= 0.75f; } } num *= CustomDifficultyMutatorManager.GetHealingMultiplier(appliedSettings, flag); return RoundScaledAmount(amount, num, allowZero: false); } public int ScaleSilkCost(int amount, string silkTakeSourceName) { if (amount <= 0) { return amount; } DifficultySettings appliedSettings = _configManager.GetAppliedSettings(); float num = 1f; string a = silkTakeSourceName ?? string.Empty; if (string.Equals(a, "ActiveUse", StringComparison.OrdinalIgnoreCase)) { num *= appliedSettings.AbilityCostMultiplier; num *= appliedSettings.SilkCostMultiplier; } else if (string.Equals(a, "Normal", StringComparison.OrdinalIgnoreCase)) { num *= appliedSettings.HealingCostMultiplier; } return RoundScaledAmount(amount, num, allowZero: true); } public int ScaleSkillCost(int amount) { if (amount <= 0) { return amount; } DifficultySettings appliedSettings = _configManager.GetAppliedSettings(); return RoundScaledAmount(amount, appliedSettings.AbilityCostMultiplier * appliedSettings.SilkCostMultiplier, allowZero: true); } public int ScaleFallbackIncomingDamage(int amount) { if (amount <= 0) { return amount; } if (_skipFallbackIncomingDamageScaling) { return amount; } DifficultySettings appliedSettings = _configManager.GetAppliedSettings(); if (appliedSettings.OneHitMode && PlayerData.HasInstance) { return Math.Max(1, GetFieldValue(HealthField, PlayerData.instance)); } float damageTakenMultiplier = appliedSettings.DamageTakenMultiplier; damageTakenMultiplier *= CustomDifficultyMutatorManager.GetIncomingDamageMultiplier(appliedSettings, isBoss: false, isEnvironmental: false); return RoundScaledAmount(amount, damageTakenMultiplier, allowZero: true); } public int ScaleDetailedIncomingDamage(GameObject source, int amount, object hazardType) { if (amount <= 0) { return amount; } DifficultySettings appliedSettings = _configManager.GetAppliedSettings(); if (appliedSettings.OneHitMode && PlayerData.HasInstance) { return Math.Max(1, GetFieldValue(HealthField, PlayerData.instance)); } float num = appliedSettings.DamageTakenMultiplier; bool flag = IsBossGameObject(source); bool flag2 = IsEnvironmentalHazard(source, hazardType); if (flag2) { num *= appliedSettings.EnvironmentalDamageMultiplier; } else if (flag) { if (TryGetBossProfile(source, out var profile)) { num *= Math.Max(0f, profile.DamageMultiplier); } else if (!appliedSettings.BossProfilesEnabled || appliedSettings.BossProfilesFallbackGlobal) { num *= appliedSettings.BossDamageTakenMultiplier; if (appliedSettings.BossBrutalMode) { num *= 1.25f; } } } else { num *= appliedSettings.CommonEnemyDamageTakenMultiplier; num *= appliedSettings.EnemyDamageMultiplier; } num *= CustomDifficultyMutatorManager.GetIncomingDamageMultiplier(appliedSettings, flag, flag2); return RoundScaledAmount(amount, num, allowZero: true); } public int ScaleCurrencyDrop(int amount) { if (amount <= 0) { return amount; } DifficultySettings appliedSettings = _configManager.GetAppliedSettings(); float num = appliedSettings.DropRate * appliedSettings.CurrencyDropMultiplier * CustomDifficultyMutatorManager.GetDropMultiplier(appliedSettings); if (IsInBossScene()) { num *= appliedSettings.BossRewardMultiplier; } return ScaleDropAmount(amount, num, allowZero: true); } public int ScaleCollectableAmount(CollectableItem item, int amount) { if (!ShouldScaleCollectable(item, amount)) { return amount; } DifficultySettings appliedSettings = _configManager.GetAppliedSettings(); return ScaleDropAmount(amount, appliedSettings.DropRate * appliedSettings.ItemDropMultiplier * CustomDifficultyMutatorManager.GetDropMultiplier(appliedSettings), allowZero: false); } public int ScaleOutgoingDamage(DamageEnemies damageSource, GameObject target, int baseDamage) { if ((Object)(object)damageSource == (Object)null || baseDamage <= 0) { return baseDamage; } DifficultySettings appliedSettings = _configManager.GetAppliedSettings(); float num = 1f; bool flag = IsBossGameObject(target); bool flag2 = IsLikelySilkSpear(damageSource); bool flag3 = flag2 || IsLikelySkillDamage(damageSource); if (flag2) { num *= appliedSettings.SilkSpearDamageMultiplier; } else if (flag3) { num *= appliedSettings.SkillDamageMultiplier; } if (flag) { if (TryGetBossProfile(target, out var profile)) { num *= Math.Max(0f, profile.HornetDamageMultiplier); num /= Math.Max(0.1f, profile.HealthMultiplier); } else if (!appliedSettings.BossProfilesEnabled || appliedSettings.BossProfilesFallbackGlobal) { num *= appliedSettings.BossDamageMultiplier; num /= Math.Max(0.1f, appliedSettings.BossHealthMultiplier); if (appliedSettings.BossBrutalMode) { num /= 1.35f; } } } else { num *= appliedSettings.CommonEnemyDamageMultiplier; num /= Math.Max(0.1f, appliedSettings.EnemyHealthMultiplier); if (appliedSettings.MutatorRandomizedEnemyHealth) { num /= GetRandomizedEnemyHealthMultiplier(target); } } num *= CustomDifficultyMutatorManager.GetOutgoingDamageMultiplier(appliedSettings, flag); if (ShouldCriticalHit(damageSource, appliedSettings.CriticalChance)) { num *= appliedSettings.CriticalDamageMultiplier; } return RoundScaledAmount(baseDamage, num, allowZero: true); } public int ScaleShopPrice(int amount) { if (amount <= 0) { return amount; } DifficultySettings appliedSettings = _configManager.GetAppliedSettings(); float multiplier = appliedSettings.ShopPriceMultiplier * CustomDifficultyMutatorManager.GetShopPriceMultiplier(appliedSettings); return RoundScaledAmount(amount, multiplier, allowZero: false); } public void PushDamageTarget(DamageEnemies damageSource, GameObject target) { if (!((Object)(object)damageSource == (Object)null)) { int instanceID = ((Object)damageSource).GetInstanceID(); if (!DamageTargetContexts.TryGetValue(instanceID, out var value)) { value = new Stack<GameObject>(); DamageTargetContexts[instanceID] = value; } value.Push(target); } } public void PopDamageTarget(DamageEnemies damageSource) { if ((Object)(object)damageSource == (Object)null) { return; } int instanceID = ((Object)damageSource).GetInstanceID(); if (DamageTargetContexts.TryGetValue(instanceID, out var value)) { if (value.Count > 0) { value.Pop(); } if (value.Count == 0) { DamageTargetContexts.Remove(instanceID); } } } public GameObject PeekDamageTarget(DamageEnemies damageSource) { if ((Object)(object)damageSource == (Object)null) { return null; } if (!DamageTargetContexts.TryGetValue(((Object)damageSource).GetInstanceID(), out var value) || value.Count == 0) { return null; } return value.Peek(); } private int ScaleDropAmount(int amount, float multiplier, bool allowZero) { float num = (float)amount * multiplier; int num2 = (int)Math.Floor(num); float num3 = num - (float)num2; if (Random.value < num3) { num2++; } if (!allowZero && amount > 0) { return Math.Max(1, num2); } return Math.Max(0, num2); } private static bool ShouldScaleCollectable(CollectableItem item, int amount) { if ((Object)(object)item == (Object)null || amount <= 0) { return false; } if (amount <= 1 && !(item is CollectableItemStack) && !item.DisplayAmount) { return false; } return true; } private void SyncBaseHealth(PlayerData playerData, bool force) { if (playerData == null) { return; } int profileId = GetProfileId(playerData); int value = 5; bool flag = _appliedBaseHealthByProfile.TryGetValue(profileId, out value); int fieldValue = GetFieldValue(MaxHealthBaseField, playerData); int fieldValue2 = GetFieldValue(MaxHealthField, playerData); int fieldValue3 = GetFieldValue(HealthField, playerData); int value2 = 0; _bonusHealthByProfile.TryGetValue(profileId, out value2); int baseHealth = _configManager.GetAppliedSettings().BaseHealth; int num = baseHealth + value2; if (num < 1) { num = 1; } bool flag2 = flag && value == baseHealth && fieldValue == num && fieldValue2 == num; if (!force && flag2 && ((Object)(object)HeroController.instance == (Object)null || _healthUiSynced)) { _lastSyncAt = Time.unscaledTime; return; } int num2 = fieldValue2 - fieldValue3; if (num2 < 0) { num2 = 0; } int num3 = 0; if (fieldValue3 > 0) { num3 = num - num2; if (num3 < 1) { num3 = 1; } if (num3 > num) { num3 = num; } } SetFieldValue(MaxHealthBaseField, playerData, num); SetFieldValue(MaxHealthField, playerData, num); SetFieldValue(HealthField, playerData, num3); SetFieldValue(PrevHealthField, playerData, num3); RefreshHealthUi(playerData, num, num3); _appliedBaseHealthByProfile[profileId] = baseHealth; _trackedHealthProfiles.Add(profileId); SaveProfileState(); EventRegister.SendEvent(EventRegisterEvents.HealthUpdate, (GameObject)null); EventRegister.SendEvent(EventRegisterEvents.UpdateBlueHealth, (GameObject)null); if ((Object)(object)HeroController.instance != (Object)null) { HeroController.instance.UpdateBlueHealth(); } _healthUiSynced = (Object)(object)HeroController.instance != (Object)null && num3 > 0; _lastSyncAt = Time.unscaledTime; } private void ApplyHeroRuntimeTuning() { HeroController instance = HeroController.instance; if ((Object)(object)instance == (Object)null) { return; } EnsureHeroDefaults(instance); if (_heroRuntimeDefaults != null) { DifficultySettings appliedSettings = _configManager.GetAppliedSettings(); float num = appliedSettings.InvincibilityTimeMultiplier; if (appliedSettings.BossBrutalMode && IsInBossScene()) { num *= 0.85f; } SetFloatField(HeroRunSpeedField, instance, _heroRuntimeDefaults.RunSpeed * appliedSettings.PlayerSpeedMultiplier); SetFloatField(HeroWalkSpeedField, instance, _heroRuntimeDefaults.WalkSpeed * appliedSettings.PlayerSpeedMultiplier); SetFloatField(HeroQuickeningRunSpeedField, instance, _heroRuntimeDefaults.QuickeningRunSpeed * appliedSettings.PlayerSpeedMultiplier); SetFloatField(HeroQuickeningWalkSpeedField, instance, _heroRuntimeDefaults.QuickeningWalkSpeed * appliedSettings.PlayerSpeedMultiplier); float num2 = appliedSettings.JumpHeightMultiplier * CustomDifficultyMutatorManager.GetJumpMultiplier(appliedSettings); SetFloatField(HeroJumpSpeedField, instance, _heroRuntimeDefaults.JumpSpeed * num2); SetFloatField(HeroMinJumpSpeedField, instance, _heroRuntimeDefaults.MinJumpSpeed * num2); SetFloatField(HeroDashCooldownField, instance, _heroRuntimeDefaults.DashCooldown * appliedSettings.DashCooldownMultiplier); SetFloatField(HeroInvulTimeField, instance, _heroRuntimeDefaults.InvulTime * num); SetFloatField(HeroInvulTimeCrossStitchField, instance, _heroRuntimeDefaults.InvulTimeCrossStitch * num); SetFloatField(HeroInvulTimeParryField, instance, _heroRuntimeDefaults.InvulTimeParry * num); SetFloatField(HeroInvulTimeQuakeField, instance, _heroRuntimeDefaults.InvulTimeQuake * num); SetFloatField(HeroInvulTimeSilkDashField, instance, _heroRuntimeDefaults.InvulTimeSilkDash * num); } } public void SyncDashCooldownTimer(HeroController hero) { if ((Object)(object)hero == (Object)null || HeroDashCooldownTimerField == null || HeroDashCooldownField == null) { return; } try { HeroDashCooldownTimerField.SetValue(hero, HeroDashCooldownField.GetValue(hero)); } catch { } } public void ScaleHarpoonDashCooldown(HeroController hero) { if ((Object)(object)hero == (Object)null || HeroHarpoonDashCooldownField == null) { return; } try { float num = Convert.ToSingle(HeroHarpoonDashCooldownField.GetValue(hero)); float dashCooldownMultiplier = _configManager.GetAppliedSettings().DashCooldownMultiplier; HeroHarpoonDashCooldownField.SetValue(hero, num * dashCooldownMultiplier); } catch { } } private void EnsureHeroDefaults(HeroController hero) { if (!((Object)(object)hero == (Object)null) && (_heroRuntimeDefaults == null || _heroRuntimeDefaults.HeroControllerId != ((Object)hero).GetInstanceID())) { HeroRuntimeDefaults heroRuntimeDefaults = new HeroRuntimeDefaults(); heroRuntimeDefaults.HeroControllerId = ((Object)hero).GetInstanceID(); heroRuntimeDefaults.RunSpeed = GetFloatField(HeroRunSpeedField, hero, 0f); heroRuntimeDefaults.WalkSpeed = GetFloatField(HeroWalkSpeedField, hero, 0f); heroRuntimeDefaults.QuickeningRunSpeed = GetFloatField(HeroQuickeningRunSpeedField, hero, heroRuntimeDefaults.RunSpeed); heroRuntimeDefaults.QuickeningWalkSpeed = GetFloatField(HeroQuickeningWalkSpeedField, hero, heroRuntimeDefaults.WalkSpeed); heroRuntimeDefaults.JumpSpeed = GetFloatField(HeroJumpSpeedField, hero, 0f); heroRuntimeDefaults.MinJumpSpeed = GetFloatField(HeroMinJumpSpeedField, hero, 0f); heroRuntimeDefaults.DashCooldown = GetFloatField(HeroDashCooldownField, hero, 0f); heroRuntimeDefaults.InvulTime = GetFloatField(HeroInvulTimeField, hero, 0f); heroRuntimeDefaults.InvulTimeCrossStitch = GetFloatField(HeroInvulTimeCrossStitchField, hero, heroRuntimeDefaults.InvulTime); heroRuntimeDefaults.InvulTimeParry = GetFloatField(HeroInvulTimeParryField, hero, heroRuntimeDefaults.InvulTime); heroRuntimeDefaults.InvulTimeQuake = GetFloatField(HeroInvulTimeQuakeField, hero, heroRuntimeDefaults.InvulTime); heroRuntimeDefaults.InvulTimeSilkDash = GetFloatField(HeroInvulTimeSilkDashField, hero, heroRuntimeDefaults.InvulTime); _heroRuntimeDefaults = heroRuntimeDefaults; } } private static void RefreshHealthUi(PlayerData playerData, int desiredMaxHealth, int newHealth) { if (playerData == null || (Object)(object)HeroController.instance == (Object)null || newHealth <= 0) { return; } try { if (HeroControllerMaxHealthKeepBlueMethod != null) { HeroControllerMaxHealthKeepBlueMethod.Invoke(HeroController.instance, null); } else if (PlayerDataMaxHealthMethod != null) { PlayerDataMaxHealthMethod.Invoke(playerData, null); } } catch { } SetFieldValue(MaxHealthBaseField, playerData, desiredMaxHealth); SetFieldValue(MaxHealthField, playerData, desiredMaxHealth); SetFieldValue(HealthField, playerData, newHealth); SetFieldValue(PrevHealthField, playerData, newHealth); } private bool IsInBossScene() { if (Time.unscaledTime - _lastBossSceneRefreshAt < 0.25f) { return _cachedBossSceneActive; } _lastBossSceneRefreshAt = Time.unscaledTime; _cachedBossSceneActive = false; try { if (BossSceneController.IsBossScene) { _cachedBossSceneActive = true; } } catch { _cachedBossSceneActive = false; } return _cachedBossSceneActive; } private bool IsBossGameObject(GameObject gameObject) { if ((Object)(object)gameObject == (Object)null) { return false; } if (HasBossName(((Object)gameObject).name)) { return true; } try { MonoBehaviour[] componentsInParent = gameObject.GetComponentsInParent<MonoBehaviour>(true); foreach (MonoBehaviour val in componentsInParent) { if (!((Object)(object)val == (Object)null)) { string name = ((object)val).GetType().Name; if (name.IndexOf("Boss", StringComparison.OrdinalIgnoreCase) >= 0) { return true; } } } } catch { } return IsInBossScene(); } private bool TryGetBossProfile(GameObject gameObject, out CustomDifficultyBossProfileData profile) { profile = null; DifficultySettings appliedSettings = _configManager.GetAppliedSettings(); if (!appliedSettings.BossProfilesEnabled || _bossProfileManager == null || (Object)(object)gameObject == (Object)null) { return false; } try { if (_bossProfileManager.TryFindProfile(gameObject, out profile) && profile != null) { ReportBossProfile(profile); return true; } } catch { return false; } return false; } private void ReportBossProfile(CustomDifficultyBossProfileData profile) { if (profile != null) { string text = ((!string.IsNullOrEmpty(profile.DisplayName)) ? profile.DisplayName : profile.InternalName); if (!string.IsNullOrEmpty(text) && !_reportedBossProfiles.Contains(text)) { _reportedBossProfiles.Add(text); CustomDifficultyPlugin.LogInfoSafe("Custom Difficulty boss profile matched: " + text); } } } private float GetRandomizedEnemyHealthMultiplier(GameObject target) { if ((Object)(object)target == (Object)null) { return Random.Range(0.75f, 1.35f); } if (_randomEnemyHealthByTarget.Count > 512) { _randomEnemyHealthByTarget.Clear(); CustomDifficultyPlugin.LogInfoSafe("Custom Difficulty cleared randomized enemy health cache to prevent growth."); } int instanceID = ((Object)target).GetInstanceID(); if (!_randomEnemyHealthByTarget.TryGetValue(instanceID, out var value)) { value = Random.Range(0.75f, 1.35f); _randomEnemyHealthByTarget[instanceID] = value; } return value; } private static bool HasBossName(string name) { if (!string.IsNullOrEmpty(name)) { if (name.IndexOf("Boss", StringComparison.OrdinalIgnoreCase) < 0) { return name.IndexOf("Statue", StringComparison.OrdinalIgnoreCase) >= 0; } return true; } return false; } private static bool IsEnvironmentalHazard(GameObject source, object hazardType) { if ((Object)(object)source == (Object)null) { return true; } string text = ((Object)source).name ?? string.Empty; if (text.IndexOf("spike", StringComparison.OrdinalIgnoreCase) >= 0 || text.IndexOf("acid", StringComparison.OrdinalIgnoreCase) >= 0 || text.IndexOf("lava", StringComparison.OrdinalIgnoreCase) >= 0 || text.IndexOf("hazard", StringComparison.OrdinalIgnoreCase) >= 0 || text.IndexOf("thorn", StringComparison.OrdinalIgnoreCase) >= 0 || text.IndexOf("trap", StringComparison.OrdinalIgnoreCase) >= 0) { return true; } string text2 = ((hazardType != null) ? hazardType.ToString() : string.Empty); if (text2.IndexOf("spike", StringComparison.OrdinalIgnoreCase) >= 0 || text2.IndexOf("acid", StringComparison.OrdinalIgnoreCase) >= 0 || text2.IndexOf("lava", StringComparison.OrdinalIgnoreCase) >= 0 || text2.IndexOf("hazard", StringComparison.OrdinalIgnoreCase) >= 0) { return true; } return false; } private static bool IsLikelySkillDamage(DamageEnemies damageSource) { string fieldStringValue = GetFieldStringValue(DamageEnemiesAttackTypeField, damageSource); string fieldStringValue2 = GetFieldStringValue(DamageEnemiesRepresentingToolField, damageSource); string fieldStringValue3 = GetFieldStringValue(DamageEnemiesSpecialTypeField, damageSource); if (fieldStringValue.IndexOf("Spell", StringComparison.OrdinalIgnoreCase) >= 0) { return true; } if (fieldStringValue3.IndexOf("Acid", StringComparison.OrdinalIgnoreCase) >= 0 || fieldStringValue3.IndexOf("Heavy", StringComparison.OrdinalIgnoreCase) >= 0 || fieldStringValue3.IndexOf("Rapid", StringComparison.OrdinalIgnoreCase) >= 0) { return true; } if (fieldStringValue2.IndexOf("Silk", StringComparison.OrdinalIgnoreCase) >= 0 || fieldStringValue2.IndexOf("Spell", StringComparison.OrdinalIgnoreCase) >= 0 || fieldStringValue2.IndexOf("Bomb", StringComparison.OrdinalIgnoreCase) >= 0) { return true; } if (!GetFieldBoolValue(DamageEnemiesUseNailDamageField, damageSource, fallback: true)) { return true; } if (!GetFieldBoolValue(DamageEnemiesIsNailAttackField, damageSource, fallback: true)) { return true; } string text = (((Object)(object)((Component)damageSource).gameObject != (Object)null) ? ((Object)((Component)damageSource).gameObject).name : string.Empty); if (text.IndexOf("spell", StringComparison.OrdinalIgnoreCase) < 0 && text.IndexOf("silk", StringComparison.OrdinalIgnoreCase) < 0) { return text.IndexOf("bomb", StringComparison.OrdinalIgnoreCase) >= 0; } return true; } private static bool IsLikelySilkSpear(DamageEnemies damageSource) { string fieldStringValue = GetFieldStringValue(DamageEnemiesRepresentingToolField, damageSource); string text = (((Object)(object)damageSource != (Object)null && (Object)(object)((Component)damageSource).gameObject != (Object)null) ? ((Object)((Component)damageSource).gameObject).name : string.Empty); if (fieldStringValue.IndexOf("harpoon", StringComparison.OrdinalIgnoreCase) < 0 && fieldStringValue.IndexOf("spear", StringComparison.OrdinalIgnoreCase) < 0 && text.IndexOf("harpoon", StringComparison.OrdinalIgnoreCase) < 0) { return text.IndexOf("spear", StringComparison.OrdinalIgnoreCase) >= 0; } return true; } private static bool ShouldCriticalHit(DamageEnemies damageSource, float criticalChance) { if ((Object)(object)damageSource == (Object)null || criticalChance <= 0f) { return false; } if (GetFieldBoolValue(DamageEnemiesDoesNotCriticalHitField, damageSource, fallback: false)) { return false; } return Random.value <= criticalChance / 100f; } private static int RoundScaledAmount(int amount, float multiplier, bool allowZero) { int val = (int)Math.Round((float)amount * multiplier, MidpointRounding.AwayFromZero); if (allowZero) { return Math.Max(0, val); } return Math.Max(1, val); } private void LoadProfileState() { if (!File.Exists(_stateFilePath)) { return; } try { string text = File.ReadAllText(_stateFilePath); HealthProfileStateFile healthProfileStateFile = JsonConvert.DeserializeObject<HealthProfileStateFile>(text); if (healthProfileStateFile == null) { return; } if (healthProfileStateFile.AppliedBaseHealthByProfile != null) { foreach (KeyValuePair<string, int> item in healthProfileStateFile.AppliedBaseHealthByProfile) { if (int.TryParse(item.Key, out var result)) { _appliedBaseHealthByProfile[result] = item.Value; } } } if (healthProfileStateFile.BonusHealthByProfile == null) { return; } foreach (KeyValuePair<string, int> item2 in healthProfileStateFile.BonusHealthByProfile) { if (int.TryParse(item2.Key, out var result2)) { _bonusHealthByProfile[result2] = Math.Max(0, item2.Value); } } } catch { } } private void SaveProfileState() { try { string directoryName = Path.GetDirectoryName(_stateFilePath); if (!Directory.Exists(directoryName)) { Directory.CreateDirectory(directoryName); } HealthProfileStateFile healthProfileStateFile = new HealthProfileStateFile(); foreach (KeyValuePair<int, int> item in _appliedBaseHealthByProfile) { healthProfileStateFile.AppliedBaseHealthByProfile[item.Key.ToString()] = item.Value; } foreach (KeyValuePair<int, int> item2 in _bonusHealthByProfile) { healthProfileStateFile.BonusHealthByProfile[item2.Key.ToString()] = Math.Max(0, item2.Value); } File.WriteAllText(_stateFilePath, JsonConvert.SerializeObject((object)healthProfileStateFile, (Formatting)1)); } catch { } } private static int GetFieldValue(FieldInfo fieldInfo, object instance) { return (int)fieldInfo.GetValue(instance); } private static void SetFieldValue(FieldInfo fieldInfo, object instance, int value) { fieldInfo.SetValue(instance, value); } private static float GetFloatField(FieldInfo fieldInfo, object instance, float fallback) { if (fieldInfo == null || instance == null) { return fallback; } try { object value = fieldInfo.GetValue(instance); if (value == null) { return fallback; } return Convert.ToSingle(value); } catch { return fallback; } } private static void SetFloatField(FieldInfo fieldInfo, object instance, float value) { if (fieldInfo == null || instance == null) { return; } try { if (fieldInfo.FieldType == typeof(float)) { fieldInfo.SetValue(instance, value); } else if (fieldInfo.FieldType == typeof(double)) { fieldInfo.SetValue(instance, (double)value); } else if (fieldInfo.FieldType == typeof(int)) { fieldInfo.SetValue(instance, (int)Math.Round(value)); } } catch { } } private static bool GetFieldBoolValue(FieldInfo fieldInfo, object instance, bool fallback) { if (fieldInfo == null || instance == null) { return fallback; } try { return Convert.ToBoolean(fieldInfo.GetValue(instance)); } catch { return fallback; } } private static string GetFieldStringValue(FieldInfo fieldInfo, object instance) { if (fieldInfo == null || instance == null) { return string.Empty; } try { object value = fieldInfo.GetValue(instance); return (value != null) ? value.ToString() : string.Empty; } catch { return string.Empty; } } private static int GetProfileId(PlayerData playerData) { return (int)ProfileIdField.GetValue(playerData); } public static void BeginDetailedIncomingDamageScaling() { _skipFallbackIncomingDamageScaling = true; } public static void EndDetailedIncomingDamageScaling() { _skipFallbackIncomingDamageScaling = false; } } [HarmonyPatch(typeof(PlayerData), "get_nailDamage")] internal static class CustomDifficultyNailDamagePatch { private static void Postfix(ref int __result) { if (CustomDifficultyGameplayHooks.Instance != null) { __result = CustomDifficultyGameplayHooks.Instance.GetScaledBaseDamage(__result); } } } [HarmonyPatch(typeof(HeroController), "AddHealth", new Type[] { typeof(int) })] internal static class CustomDifficultyHeroHealingPatch { private static void Prefix(ref int amount) { if (CustomDifficultyGameplayHooks.Instance != null) { amount = CustomDifficultyGameplayHooks.Instance.ScaleHealingAmount(amount); } } } [HarmonyPatch(typeof(PlayerData), "TakeHealth", new Type[] { typeof(int), typeof(bool), typeof(bool) })] internal static class CustomDifficultyIncomingDamageFallbackPatch { private static void Prefix(ref int amount) { if (CustomDifficultyGameplayHooks.Instance != null) { amount = CustomDifficultyGameplayHooks.Instance.ScaleFallbackIncomingDamage(amount); } } } [HarmonyPatch(typeof(HeroController), "TakeDamage", new Type[] { typeof(GameObject), typeof(CollisionSide), typeof(int), typeof(HazardType), typeof(DamagePropertyFlags) })] internal static class CustomDifficultyHeroTakeDamagePatch { private static void Prefix(GameObject __0, ref int __2, HazardType __3) { //IL_0016: Unknown result type (might be due to invalid IL or missing references) if (CustomDifficultyGameplayHooks.Instance != null) { CustomDifficultyGameplayHooks.BeginDetailedIncomingDamageScaling(); __2 = CustomDifficultyGameplayHooks.Instance.ScaleDetailedIncomingDamage(__0, __2, __3); } } private static void Postfix() { CustomDifficultyGameplayHooks.EndDetailedIncomingDamageScaling(); } } [HarmonyPatch(typeof(PlayerData), "AddToMaxHealth", new Type[] { typeof(int) })] internal static class CustomDifficultyMaxHealthUpgradePatch { private static void Postfix(int amount) { if (CustomDifficultyGameplayHooks.Instance != null) { CustomDifficultyGameplayHooks.Instance.RegisterPermanentHealthUpgrade(amount); } } } [HarmonyPatch(typeof(HeroController), "TakeSilk", new Type[] { typeof(int), typeof(SilkTakeSource) })] internal static class CustomDifficultyTakeSilkPatch { private static void Prefix(ref int __0, SilkTakeSource __1) { //IL_0010: Unknown result type (might be due to invalid IL or missing references) if (CustomDifficultyGameplayHooks.Instance != null) { __0 = CustomDifficultyGameplayHooks.Instance.ScaleSilkCost(__0, ((object)__1).ToString()); } } } [HarmonyPatch(typeof(PlayerData), "get_SilkSkillCost")] internal static class CustomDifficultySilkSkillCostPatch { private static void Postfix(ref int __result) { if (CustomDifficultyGameplayHooks.Instance != null) { __result = CustomDifficultyGameplayHooks.Instance.ScaleSkillCost(__result); } } } [HarmonyPatch(typeof(CurrencyManager), "AddCurrency", new Type[] { typeof(int), typeof(CurrencyType), typeof(bool) })] internal static class CustomDifficultyCurrencyPatch { private static void Prefix(ref int __0) { if (CustomDifficultyGameplayHooks.Instance != null) { __0 = CustomDifficultyGameplayHooks.Instance.ScaleCurrencyDrop(__0); } } } [HarmonyPatch] internal static class CustomDifficultyShopPricePatch { private static IEnumerable<MethodBase> TargetMethods() { Type shopItemType = AccessTools.TypeByName("ShopItem"); MethodInfo shopItemCostGetter = ((shopItemType != null) ? AccessTools.PropertyGetter(shopItemType, "Cost") : null); if (shopItemCostGetter != null) { yield return shopItemCostGetter; } Type caravanInfoType = AccessTools.TypeByName("CaravanTroupeHunter+ShopItemInfo"); MethodInfo caravanGetCost = ((caravanInfoType != null) ? AccessTools.Method(caravanInfoType, "GetCost", (Type[])null, (Type[])null) : null); if (caravanGetCost != null) { yield return caravanGetCost; } Type simpleQuestInfoType = AccessTools.TypeByName("SimpleQuestsShopOwner+ShopItemInfo"); MethodInfo simpleQuestGetCost = ((simpleQuestInfoType != null) ? AccessTools.Method(simpleQuestInfoType, "GetCost", (Type[])null, (Type[])null) : null); if (simpleQuestGetCost != null) { yield return simpleQuestGetCost; } } private static void Postfix(ref int __result) { if (CustomDifficultyGameplayHooks.Instance != null) { __result = CustomDifficultyGameplayHooks.Instance.ScaleShopPrice(__result); } } } [HarmonyPatch(typeof(CollectableItem), "Collect", new Type[] { typeof(int), typeof(bool) })] internal static class CustomDifficultyCollectablePatch { private static void Prefix(CollectableItem __instance, ref int __0) { if (CustomDifficultyGameplayHooks.Instance != null) { __0 = CustomDifficultyGameplayHooks.Instance.ScaleCollectableAmount(__instance, __0); } } } [HarmonyPatch(typeof(DamageEnemies), "DoDamage", new Type[] { typeof(GameObject), typeof(bool) })] internal static class CustomDifficultyDamageEnemiesGameObjectPatch { private static void Prefix(DamageEnemies __instance, GameObject __0) { if (CustomDifficultyGameplayHooks.Instance != null) { CustomDifficultyGameplayHooks.Instance.PushDamageTarget(__instance, __0); } } private static void Postfix(DamageEnemies __instance) { if (CustomDifficultyGameplayHooks.Instance != null) { CustomDifficultyGameplayHooks.Instance.PopDamageTarget(__instance); } } } [HarmonyPatch(typeof(DamageEnemies), "DoDamage", new Type[] { typeof(Collider2D), typeof(bool) })] internal static class CustomDifficultyDamageEnemiesColliderPatch { private static void Prefix(DamageEnemies __instance, Collider2D __0) { if (CustomDifficultyGameplayHooks.Instance != null) { CustomDifficultyGameplayHooks.Instance.PushDamageTarget(__instance, ((Object)(object)__0 != (Object)null) ? ((Component)__0).gameObject : null); } } private static void Postfix(DamageEnemies __instance) { if (CustomDifficultyGameplayHooks.Instance != null) { CustomDifficultyGameplayHooks.Instance.PopDamageTarget(__instance); } } } [HarmonyPatch(typeof(DamageEnemies), "TryDoDamage", new Type[] { typeof(Collider2D), typeof(bool) })] internal static class CustomDifficultyTryDamageEnemiesPatch { private static void Prefix(DamageEnemies __instance, Collider2D __0) { if (CustomDifficultyGameplayHooks.Instance != null) { CustomDifficultyGameplayHooks.Instance.PushDamageTarget(__instance, ((Object)(object)__0 != (Object)null) ? ((Component)__0).gameObject : null); } } private static void Postfix(DamageEnemies __instance) { if (CustomDifficultyGameplayHooks.Instance != null) { CustomDifficultyGameplayHooks.Instance.PopDamageTarget(__instance); } } } [HarmonyPatch(typeof(DamageEnemies), "GetDamageDealt")] internal static class CustomDifficultyOutgoingDamagePatch { private static void Postfix(DamageEnemies __instance, ref int __result) { if (CustomDifficultyGameplayHooks.Instance != null) { GameObject target = CustomDifficultyGameplayHooks.Instance.PeekDamageTarget(__instance); __result = CustomDifficultyGameplayHooks.Instance.ScaleOutgoingDamage(__instance, target, __result); } } } [HarmonyPatch(typeof(HeroController), "SetDashCooldownTimer")] internal static class CustomDifficultyDashCooldownPatch { private static void Postfix(HeroController __instance) { if (CustomDifficultyGameplayHooks.Instance != null && !((Object)(object)__instance == (Object)null)) { CustomDifficultyGameplayHooks.Instance.SyncDashCooldownTimer(__instance); } } } [HarmonyPatch(typeof(HeroController), "StartHarpoonDashCooldown")] internal static class CustomDifficultyHarpoonDashCooldownPatch { private static void Postfix(HeroController __instance) { if (CustomDifficultyGameplayHooks.Instance != null && !((Object)(object)__instance == (Object)null)) { CustomDifficultyGameplayHooks.Instance.ScaleHarpoonDashCooldown(__instance); } } } internal sealed class CustomDifficultyLocalizationManager { private sealed class MenuLanguageOption { public string Code; public string DisplayName; public bool AiCreated; } private readonly BaseUnityPlugin _plugin; private readonly string _localizationDirectory; private readonly ConfigEntry<string> _menuLanguageOverride; private readonly Dictionary<string, string> _englishStrings; private readonly List<MenuLanguageOption> _availableLanguages; private Dictionary<string, string> _currentStrings; private string _currentLanguageCode; public CustomDifficultyLocalizationManager(BaseUnityPlugin plugin, string localizationDirectory) { _plugin = plugin; _localizationDirectory = localizationDirectory; _menuLanguageOverride = plugin.Config.Bind<string>("Menu", "MenuLanguage", "auto", "Menu language override. Use 'auto' to follow the game's current language."); _englishStrings = LoadLanguageFile("en"); _availableLanguages = BuildAvailableLanguages(); _currentStrings = _englishStrings; _currentLanguageCode = "en"; UpdateLanguage(force: true); } public void UpdateLanguage(bool force) { string mappedLanguageCode = GetMappedLanguageCode(); if (force || !string.Equals(mappedLanguageCode, _currentLanguageCode, StringComparison.OrdinalIgnoreCase)) { _currentStrings = LoadLanguageFile(mappedLanguageCode); _currentLanguageCode = mappedLanguageCode; } } public void CycleLanguage(int direction) { if (_availableLanguages.Count == 0) { return; } string normalizedOverrideCode = GetNormalizedOverrideCode(); int num = 0; for (int i = 0; i < _availableLanguages.Count; i++) { if (string.Equals(_availableLanguages[i].Code, normalizedOverrideCode, StringComparison.OrdinalIgnoreCase)) { num = i; break; } } num += ((direction >= 0) ? 1 : (-1)); if (num < 0) { num = _availableLanguages.Count - 1; } else if (num >= _availableLanguages.Count) { num = 0; } _menuLanguageOverride.Value = _availableLanguages[num].Code; _plugin.Config.Save(); UpdateLanguage(force: true); } public void ResetLanguageOverride() { _menuLanguageOverride.Value = "auto"; _plugin.Config.Save(); UpdateLanguage(force: true); } public string GetCurrentMenuLanguageDisplayName() { return GetLanguageDisplayName(GetNormalizedOverrideCode()); } public string GetDefaultMenuLanguageDisplayName() { return GetLanguageDisplayName("auto"); } public string Get(string key) { if (_currentStrings != null && _currentStrings.TryGetValue(key, out var value)) { return value; } if (_englishStrings != null && _englishStrings.TryGetValue(key, out value)) { return value; } return key; } private string GetLanguageDisplayName(string code) { if (string.Equals(code, "auto", StringComparison.OrdinalIgnoreCase)) { return Get("language_auto"); } MenuLanguageOption menuLanguageOption = FindLanguageOption(code); if (menuLanguageOption == null) { return code; } if (!menuLanguageOption.AiCreated) { return menuLanguageOption.DisplayName; } return menuLanguageOption.DisplayName + Get("ai_created_suffix"); } private Dictionary<string, string> LoadLanguageFile(string languageCode) { string path = Path.Combine(_localizationDirectory, languageCode + ".json"); if (!File.Exists(path)) { return _englishStrings ?? new Dictionary<string, string>(); } try { string text = File.ReadAllText(path); Dictionary<string, string> dictionary = JsonConvert.DeserializeObject<Dictionary<string, string>>(text); return dictionary ?? _englishStrings ?? new Dictionary<string, string>(); } catch { return _englishStrings ?? new Dictionary<string, string>(); } } private string GetMappedLanguageCode() { string normalizedOverrideCode = GetNormalizedOverrideCode(); if (!string.Equals(normalizedOverrideCode, "auto", StringComparison.OrdinalIgnoreCase)) { return normalizedOverrideCode; } return MapGameLanguageCode(); } private string GetNormalizedOverrideCode() { string value = _menuLanguageOverride.Value; if (string.IsNullOrWhiteSpace(value)) { return "auto"; } for (int i = 0; i < _availableLanguages.Count; i++) { if (string.Equals(_availableLanguages[i].Code, value, StringComparison.OrdinalIgnoreCase)) { return _availableLanguages[i].Code; } } return "auto"; } private MenuLanguageOption FindLanguageOption(string code) { for (int i = 0; i < _availableLanguages.Count; i++) { if (string.Equals(_availableLanguages[i].Code, code, StringComparison.OrdinalIgnoreCase)) { return _availableLanguages[i]; } } return null; } private static List<MenuLanguageOption> BuildAvailableLanguages() { List<MenuLanguageOption> list = new List<MenuLanguageOption>(); list.Add(new MenuLanguageOption { Code = "auto", DisplayName = "Auto", AiCreated = false }); list.Add(new MenuLanguageOption { Code = "pt-BR", DisplayName = "Português (Brasil)", AiCreated = false }); list.Add(new MenuLanguageOption { Code = "en", DisplayName = "English", AiCreated = true }); list.Add(new MenuLanguageOption { Code = "es", DisplayName = "Español", AiCreated = true }); list.Add(new MenuLanguageOption { Code = "fr", DisplayName = "Français", AiCreated = true }); list.Add(new MenuLanguageOption { Code = "de", DisplayName = "Deutsch", AiCreated = true }); list.Add(new MenuLanguageOption { Code = "it", DisplayName = "Italiano", AiCreated = true }); list.Add(new MenuLanguageOption { Code = "ru", DisplayName = "Русский", AiCreated = true }); list.Add(new MenuLanguageOption { Code = "ja", DisplayName = "日本語", AiCreated = true }); list.Add(new MenuLanguageOption { Code = "ko", DisplayName = "한국어", AiCreated = true }); list.Add(new MenuLanguageOption { Code = "zh-CN", DisplayName = "简体中文", AiCreated = true }); list.Add(new MenuLanguageOption { Code = "zh-TW", DisplayName = "繁體中文", AiCreated = true }); return list; } private static string MapGameLanguageCode() { //IL_0000: Unknown result type (might be due to invalid IL or missing references) try { string text = ((object)Language.CurrentLanguage()).ToString(); if (text.StartsWith("FR", StringComparison.OrdinalIgnoreCase)) { return "fr"; } if (text.StartsWith("DE", StringComparison.OrdinalIgnoreCase)) { return "de"; } if (text.StartsWith("IT", StringComparison.OrdinalIgnoreCase)) { return "it"; } if (text.StartsWith("PT", StringComparison.OrdinalIgnoreCase)) { return "pt-BR"; } if (text.StartsWith("ES", StringComparison.OrdinalIgnoreCase)) { return "es"; } if (text.StartsWith("RU", StringComparison.OrdinalIgnoreCase)) { return "ru"; } if (text.StartsWith("JA", StringComparison.OrdinalIgnoreCase)) { return "ja"; } if (text.StartsWith("KO", StringComparison.OrdinalIgnoreCase)) { return "ko"; } if (text.StartsWith("ZH_TW", StringComparison.OrdinalIgnoreCase) || text.StartsWith("ZH_HK", StringComparison.OrdinalIgnoreCase)) { return "zh-TW"; } if (text.StartsWith("ZH", StringComparison.OrdinalIgnoreCase)) { return "zh-CN"; } } catch { } return "en"; } } internal sealed class CustomDifficultyMenuUI { private enum MenuCategory { Summary, Favorites, Player, Damage, Enemies, Bosses, Drops, Economy, Movement, Mutators, Presets, BossProfiles, Context, Adaptive, System } private sealed class MenuOptionDefinition { public string Id; public string LabelKey; public string DescriptionKey; public MenuCategory Category; public CustomDifficultyOptionStatus Status; public bool ActionOption; public bool PresetOption; public bool InfoOption; public DifficultyPreset Preset; } private readonly BaseUnityPlugin _plugin; private readonly CustomDifficultyConfigManager _configManager; private readonly CustomDifficultyLocalizationManager _localizationManager; private readonly CustomDifficultyGameplayHooks _gameplayHooks; private readonly CustomDifficultyFavorites _favorites; private readonly CustomDifficultyPresetManager _presetManager; private readonly CustomDifficultyBossProfileManager _bossProfileManager; private readonly object _inputBlockerToken = new object(); private readonly List<MenuOptionDefinition> _allOptions = new List<MenuOptionDefinition>(); private readonly MenuCategory[] _categories = new MenuCategory[15] { MenuCategory.Summary, MenuCategory.Favorites, MenuCategory.Player, MenuCategory.Damage, MenuCategory.Enemies, MenuCategory.Bosses, MenuCategory.Drops, MenuCategory.Economy, MenuCategory.Movement, MenuCategory.Mutators, MenuCategory.Presets, MenuCategory.BossProfiles, MenuCategory.Context, MenuCategory.Adaptive, MenuCategory.System }; private bool _isOpen; private bool _inputBlocked; private bool _textFieldFocused; private bool _focusSearchRequested; private bool _clearTextFocusRequested; private bool _timeScalePaused; private float _previousTimeScale = 1f; private int _selectedCategoryIndex; private int _selectedOptionIndex; private string _searchText = string.Empty; private string _presetNameInput = "Meu Preset"; private float _lastVerticalInputTime = -99f; private float _lastHorizontalInputTime = -99f; private float _lastCategoryInputTime = -99f; private GUIStyle _titleStyle; private GUIStyle _detailTitleStyle; private GUIStyle _subtitleStyle; private GUIStyle _tabStyle; private GUIStyle _selectedTabStyle; private GUIStyle _rowStyle; private GUIStyle _selectedRowStyle; private GUIStyle _valueStyle; private GUIStyle _descriptionStyle; private GUIStyle _buttonStyle; private GUIStyle _selectedButtonStyle; private GUIStyle _badgeStyle; private GUIStyle _smallInputStyle; public CustomDifficultyMenuUI(BaseUnityPlugin plugin, CustomDifficultyConfigManager configManager, CustomDifficultyLocalizationManager localizationManager, CustomDifficultyGameplayHooks gameplayHooks, CustomDifficultyFavorites favorites, CustomDifficultyPresetManager presetManager, CustomDifficultyBossProfileManager bossProfileManager) { _plugin = plugin; _configManager = configManager; _localizationManager = localizationManager; _gameplayHooks = gameplayHooks; _favorites = favorites; _presetManager = presetManager; _bossProfileManager = bossProfileManager; BuildOptionDefinitions(); } public void Update() { //IL_000e: Unknown result type (might be due to invalid IL or missing references) //IL_005c: Unknown result type (might be due to invalid IL or missing references) if (!_isOpen) { if (Input.GetKeyDown(_configManager.GetOpenMenuKey())) { Open(); } return; } if (_textFieldFocused) { if (Input.GetKeyDown((KeyCode)27)) { _clearTextFocusRequested = true; _textFieldFocused = false; } return; } if (Input.GetKeyDown((KeyCode)27) || Input.GetKeyDown((KeyCode)324) || Input.GetKeyDown(_configManager.GetOpenMenuKey()) || Input.GetKeyDown((KeyCode)331)) { Close(keepDraft: false); return; } if (Input.GetKeyDown((KeyCode)114)) { ResetSelectedOptionToDefault(); } if (Input.GetKeyDown((KeyCode)102)) { ToggleSelectedFavorite(); } if (Input.GetKeyDown((KeyCode)47)) { _focusSearchRequested = true; } HandleCategoryNavigation(); HandleVerticalNavigation(); HandleHorizontalAdjustment(); if (Input.GetKeyDown((KeyCode)13) || Input.GetKeyDown((KeyCode)271) || Input.GetKeyDown((KeyCode)32) || Input.GetKeyDown((KeyCode)330)) { ExecuteSelectedOption(); } } public void OnGUI() { //IL_0019: 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_0033: Unknown result type (might be due to invalid IL or missing references) //IL_0053: Unknown result type (might be due to invalid IL or missing references) //IL_0062: Unknown result type (might be due to invalid IL or missing references) //IL_00bb: Unknown result type (might be due to invalid IL or missing references) //IL_00d0: Unknown result type (might be due to invalid IL or missing references) //IL_00e9: Unknown result type (might be due to invalid IL or missing references) //IL_0124: Unknown result type (might be due to invalid IL or missing references) //IL_013d: Unknown result type (might be due to invalid IL or missing references) //IL_0173: Unknown result type (might be due to invalid IL or missing references) //IL_01bf: Unknown result type (might be due to invalid IL or missing references) //IL_01d6: Unknown result type (might be due to invalid IL or missing references) //IL_0263: Unknown result type (might be due to invalid IL or missing references) //IL_0323: Unknown result type (might be due to invalid IL or missing references) //IL_032b: Unknown result type (might be due to invalid IL or missing references) //IL_0333: Unknown result type (might be due to invalid IL or missing references) //IL_0216: Unknown result type (might be due to invalid IL or missing references) //IL_023f: Unknown result type (might be due to invalid IL or missing references) //IL_0258: Unknown result type (might be due to invalid IL or missing references) if (_isOpen) { EnsureStyles(); GUI.depth = -1000; Color color = GUI.color; GUI.color = new Color(0f, 0f, 0f, 0.78f); GUI.DrawTexture(new Rect(0f, 0f, (float)Screen.width, (float)Screen.height), (Texture)(object)Texture2D.whiteTexture); GUI.color = color; float num = Mathf.Min(1240f, (float)Screen.width - 72f); float num2 = Mathf.Min(780f, (float)Screen.height - 72f); Rect val = default(Rect); ((Rect)(ref val))..ctor(((float)Screen.width - num) * 0.5f, ((float)Screen.height - num2) * 0.5f, num, num2); DrawPanel(val, new Color(0.05f, 0.07f, 0.08f, 0.96f), new Color(0.87f, 0.77f, 0.45f, 0.45f), 2f); DrawSolidRect(new Rect(((Rect)(ref val)).x + 22f, ((Rect)(ref val)).y + 100f, ((Rect)(ref val)).width - 44f, 1f), new Color(1f, 1f, 1f, 0.08f)); GUI.Label(new Rect(((Rect)(ref val)).x + 28f, ((Rect)(ref val)).y + 14f, ((Rect)(ref val)).width - 560f, 46f), _localizationManager.Get("menu_title"), _titleStyle); GUI.Label(new Rect(((Rect)(ref val)).x + 30f, ((Rect)(ref val)).y + 62f, ((Rect)(ref val)).width - 520f, 34f), GetVisibleCategoryLabel(), _subtitleStyle); DrawSearchBox(val); if (_configManager.HasUnsavedChanges()) { DrawBadge(new Rect(((Rect)(ref val)).x + ((Rect)(ref val)).width - 190f, ((Rect)(ref val)).y + 62f, 160f, 28f), _localizationManager.Get("unsaved"), new Color(0.28f, 0.18f, 0.08f, 0.98f), new Color(0.95f, 0.83f, 0.42f, 0.9f)); } DrawCategoryTabs(val); float num3 = ((Rect)(ref val)).y + 164f; float num4 = ((Rect)(ref val)).height - 222f; float num5 = Mathf.Clamp(((Rect)(ref val)).width * 0.35f, 400f, 460f); Rect listRect = default(Rect); ((Rect)(ref listRect))..ctor(((Rect)(ref val)).x + 24f, num3, ((Rect)(ref val)).width - num5 - 66f, num4); Rect detailRect = default(Rect); ((Rect)(ref detailRect))..ctor(((Rect)(ref listRect)).xMax + 18f, num3, num5, num4); Rect footerRect = default(Rect); ((Rect)(ref footerRect))..ctor(((Rect)(ref val)).x + 24f, ((Rect)(ref val)).y + ((Rect)(ref val)).height - 50f, ((Rect)(ref val)).width - 48f, 30f); DrawCurrentOptions(listRect); DrawDescription(detailRect); DrawFooter(footerRect); if (_focusSearchRequested) { GUI.FocusControl("CDSearch"); _focusSearchRequested = false; } if (_clearTextFocusRequested) { GUI.FocusControl(string.Empty); _clearTextFocusRequested = false; } string nameOfFocusedControl = GUI.GetNameOfFocusedControl(); _textFieldFocused = string.Equals(nameOfFocusedControl, "CDSearch", StringComparison.Ordinal) || string.Equals(nameOfFocusedControl, "CDPresetName", StringComparison.Ordinal); ConsumeUnityGuiInputEvent(); } } public void Shutdown() { _isOpen = false; SetHeroInputBlocked(blocked: false); RestoreGameTime(); } private void DrawSearchBox(Rect panelRect) { //IL_002c: Unknown result type (might be due to invalid IL or missing references) //IL_0083: Unknown result type (might be due to invalid IL or missing references) //IL_00d6: Unknown result type (might be due to invalid IL or missing references) //IL_0133: Unknown result type (might be due to invalid IL or missing references) GUI.Label(new Rect(((Rect)(ref panelRect)).x + ((Rect)(ref panelRect)).width - 475f, ((Rect)(ref panelRect)).y + 22f, 58f, 28f), _localizationManager.Get("search_label"), _descriptionStyle); GUI.SetNextControlName("CDSearch"); _searchText = GUI.TextField(new Rect(((Rect)(ref panelRect)).x + ((Rect)(ref panelRect)).width - 410f, ((Rect)(ref panelRect)).y + 18f, 230f, 32f), _searchText ?? string.Empty, 32, _smallInputStyle); if (DrawFlatButton(new Rect(((Rect)(ref panelRect)).x + ((Rect)(ref panelRect)).width - 170f, ((Rect)(ref panelRect)).y + 18f, 70f, 32f), _localizationManager.Get("clear_search"), accent: false, highlighted: false)) { _searchText = string.Empty; _clearTextFocusRequested = true; } if (DrawFlatButton(new Rect(((Rect)(ref panelRect)).x + ((Rect)(ref panelRect)).width - 92f, ((Rect)(ref panelRect)).y + 18f, 62f, 32f), _localizationManager.Get("favorites_short"), _categories[_selectedCategoryIndex] == MenuCategory.Favorites, highlighted: false)) { SelectCategory(MenuCategory.Favorites); } } private void Open() { _configManager.ResetDraftToApplied(); _presetManager.RefreshPresets(); _selectedCategoryIndex = 0; _selectedOptionIndex = 0; _isOpen = true; PauseGameTime(); SetHeroInputBlocked(blocked: true); Cursor.visible = true; } private void Close(bool keepDraft) { if (!keepDraft) { _configManager.ResetDraftToApplied(); } _isOpen = false; SetHeroInputBlocked(blocked: false); RestoreGameTime(); Cursor.visible = false; } private void PauseGameTime() { if (!_timeScalePaused) { _previousTimeScale = Time.timeScale; Time.timeScale = 0f; _timeScalePaused = true; } } private void RestoreGameTime() { if (_timeScalePaused) { Time.timeScale = ((_previousTimeScale <= 0f) ? 1f : _previousTimeScale); _timeScalePaused = false; } } private static void ConsumeUnityGuiInputEvent() { //IL_001b: Unknown result type (might be due to invalid IL or missing references) //IL_0021: Invalid comparison between Unknown and I4 Event current = Event.current; if (current != null && (current.isKey || current.isMouse || (int)current.type == 6)) { current.Use(); } } private void SetHeroInputBlocked(bool blocked) { if ((Object)(object)HeroController.instance == (Object)null) { _inputBlocked = false; } else if (blocked && !_inputBlocked) { HeroController.instance.AddInputBlocker(_inputBlockerToken); _inputBlocked = true; } else if (!blocked && _inputBlocked) { HeroController.instance.RemoveInputBlocker(_inputBlockerToken); _inputBlocked = false; } } private void HandleCategoryNavigation() { int num = 0; if (Input.GetKeyDown((KeyCode)113) || Input.GetKeyDown((KeyCode)280)) { num = -1; } else if (Input.GetKeyDown((KeyCode)101) || Input.GetKeyDown((KeyCode)281) || Input.GetKeyDown((KeyCode)9)) { num = 1; } if (num != 0 && !(Time.unscaledTime - _lastCategoryInputTime < 0.12f)) { _selectedCategoryIndex += num; if (_selectedCategoryIndex < 0) { _selectedCategoryIndex = _categories.Length - 1; } else if (_selectedCategoryIndex >= _categories.Length) { _selectedCategoryIndex = 0; } _selectedOptionIndex = 0; _lastCategoryInputTime = Time.unscaledTime; } } private void HandleVerticalNavigation() { int num = 0; if (Input.GetKeyDown((KeyCode)273) || Input.GetKeyDown((KeyCode)119)) { num = -1; } else if (Input.GetKeyDown((KeyCode)274) || Input.GetKeyDown((KeyCode)115)) { num = 1; } else { float axisSafe = GetAxisSafe("Vertical"); if (Math.Abs(axisSafe) > 0.6f && Time.unscaledTime - _lastVerticalInputTime > 0.18f) { num = ((!(axisSafe > 0f)) ? 1 : (-1)); } } if (num == 0) { return; } List<MenuOptionDefinition> currentOptions = GetCurrentOptions(); if (currentOptions.Count != 0) { _selectedOptionIndex += num; if (_selectedOptionIndex < 0) { _selectedOptionIndex = currentOptions.Count - 1; } else if (_selectedOptionIndex >= currentOptions.Count) { _selectedOptionIndex = 0; } _lastVerticalInputTime = Time.unscaledTime; } } private void HandleHorizontalAdjustment() { MenuOptionDefinition selectedOption = GetSelectedOption(); if (selectedOption == null || selectedOption.ActionOption || selectedOption.PresetOption || selectedOption.InfoOption) { return; } int num = 0; if (Input.GetKeyDown((KeyCode)276) || Input.GetKeyDown((KeyCode)97)) { num = -1; } else if (Input.GetKeyDown((KeyCode)275) || Input.GetKeyDown((KeyCode)100)) { num = 1; } else { float axisSafe = GetAxisSafe("Horizontal"); if (Math.Abs(axisSafe) > 0.6f && Time.unscaledTime - _lastHorizontalInputTime > 0.12f) { num = ((axisSafe > 0f) ? 1 : (-1)); } } if (num != 0) { AdjustOption(selectedOption, num); _lastHorizontalInputTime = Time.u