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 Troll Toll v0.20.4
TrollToll.dll
Decompiled 3 weeks agousing System; using System.Collections.Generic; using System.Diagnostics; using System.Globalization; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Security; using System.Security.Permissions; using System.Text; using BepInEx; using BepInEx.Configuration; using HarmonyLib; using Microsoft.CodeAnalysis; using UnityEngine; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)] [assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")] [assembly: AssemblyCompany("BeardyMike")] [assembly: AssemblyConfiguration("Debug")] [assembly: AssemblyDescription("Stamina mod for Valheim that turns off stamina drain when no enemies are nearby, with granular biome, creature, and boss relief progression control.")] [assembly: AssemblyFileVersion("0.20.4")] [assembly: AssemblyInformationalVersion("0.20.4+17bc48ff1bed24fb97f0e8342682a777b573321a")] [assembly: AssemblyProduct("Troll-Toll")] [assembly: AssemblyTitle("TrollToll")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("0.20.4.0")] [module: UnverifiableCode] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)] internal sealed class RefSafetyRulesAttribute : Attribute { public readonly int Version; public RefSafetyRulesAttribute(int P_0) { Version = P_0; } } } namespace TrollToll { public static class BiomeManager { public class BiomeSetting { public ConfigEntry<bool> Enabled; public ConfigEntry<float> DetectionRadius; } private static readonly Dictionary<Biome, BiomeSetting> _biomeSettings = new Dictionary<Biome, BiomeSetting>(); public static void Initialize(ConfigFile config) { _biomeSettings.Clear(); RegisterBiome(config, (Biome)1, "Meadows", 30f); RegisterBiome(config, (Biome)8, "BlackForest", 30f); RegisterBiome(config, (Biome)2, "Swamp", 30f); RegisterBiome(config, (Biome)4, "Mountain", 30f); RegisterBiome(config, (Biome)16, "Plains", 30f); RegisterBiome(config, (Biome)512, "Mistlands", 30f); RegisterBiome(config, (Biome)32, "Ashlands", 30f); RegisterBiome(config, (Biome)64, "DeepNorth", 30f); RegisterBiome(config, (Biome)256, "Ocean", 30f); } private static void RegisterBiome(ConfigFile config, Biome biome, string displayName, float defaultRadius) { //IL_0066: Unknown result type (might be due to invalid IL or missing references) BiomeSetting value = new BiomeSetting { Enabled = config.Bind<bool>("5 - Biome Controls", displayName + "_Enabled", true, "Enable Troll-Toll stamina mechanics in the " + displayName + " biome."), DetectionRadius = config.Bind<float>("5 - Biome Controls", displayName + "_DetectionRadius", defaultRadius, "Threat detection radius in meters for the " + displayName + " biome. Set to -1 to use global DetectionRadius.") }; _biomeSettings[biome] = value; } public static bool IsBiomeModEnabled(Biome biome) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_000e: Unknown result type (might be due to invalid IL or missing references) biome = GetPrimaryBiome(biome); if (_biomeSettings.TryGetValue(biome, out var value)) { return value.Enabled.Value; } return true; } public static float GetBiomeDetectionRadius(Biome biome) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_000e: Unknown result type (might be due to invalid IL or missing references) biome = GetPrimaryBiome(biome); if (_biomeSettings.TryGetValue(biome, out var value)) { float value2 = value.DetectionRadius.Value; if (value2 > 0f) { return value2; } } return TrollTollPlugin.DetectionRadius.Value; } public static Biome GetPrimaryBiome(Biome biome) { //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_0017: Unknown result type (might be due to invalid IL or missing references) //IL_001a: Unknown result type (might be due to invalid IL or missing references) //IL_001c: Invalid comparison between Unknown and I4 //IL_0010: Unknown result type (might be due to invalid IL or missing references) //IL_0011: Unknown result type (might be due to invalid IL or missing references) //IL_002a: Unknown result type (might be due to invalid IL or missing references) //IL_0030: Unknown result type (might be due to invalid IL or missing references) //IL_0032: Invalid comparison between Unknown and I4 //IL_0024: Unknown result type (might be due to invalid IL or missing references) //IL_00c3: Unknown result type (might be due to invalid IL or missing references) //IL_0043: Unknown result type (might be due to invalid IL or missing references) //IL_0046: Unknown result type (might be due to invalid IL or missing references) //IL_0048: Invalid comparison between Unknown and I4 //IL_003d: Unknown result type (might be due to invalid IL or missing references) //IL_0055: Unknown result type (might be due to invalid IL or missing references) //IL_0058: Unknown result type (might be due to invalid IL or missing references) //IL_005a: Invalid comparison between Unknown and I4 //IL_0052: Unknown result type (might be due to invalid IL or missing references) //IL_0067: Unknown result type (might be due to invalid IL or missing references) //IL_0069: Unknown result type (might be due to invalid IL or missing references) //IL_006b: Invalid comparison between Unknown and I4 //IL_0064: Unknown result type (might be due to invalid IL or missing references) //IL_0077: Unknown result type (might be due to invalid IL or missing references) //IL_0079: Unknown result type (might be due to invalid IL or missing references) //IL_007b: Invalid comparison between Unknown and I4 //IL_0074: Unknown result type (might be due to invalid IL or missing references) //IL_0087: Unknown result type (might be due to invalid IL or missing references) //IL_0089: Unknown result type (might be due to invalid IL or missing references) //IL_008b: Invalid comparison between Unknown and I4 //IL_0084: Unknown result type (might be due to invalid IL or missing references) //IL_0097: Unknown result type (might be due to invalid IL or missing references) //IL_0099: Unknown result type (might be due to invalid IL or missing references) //IL_009b: Invalid comparison between Unknown and I4 //IL_0094: Unknown result type (might be due to invalid IL or missing references) //IL_00a7: Unknown result type (might be due to invalid IL or missing references) //IL_00ad: Unknown result type (might be due to invalid IL or missing references) //IL_00af: Invalid comparison between Unknown and I4 //IL_00a4: Unknown result type (might be due to invalid IL or missing references) //IL_00c0: Unknown result type (might be due to invalid IL or missing references) //IL_00bc: Unknown result type (might be due to invalid IL or missing references) if (_biomeSettings.ContainsKey(biome)) { return biome; } if ((biome & 0x20) > 0) { return (Biome)32; } if ((biome & 0x200) > 0) { return (Biome)512; } if ((biome & 0x40) > 0) { return (Biome)64; } if ((biome & 0x10) > 0) { return (Biome)16; } if ((biome & 4) > 0) { return (Biome)4; } if ((biome & 2) > 0) { return (Biome)2; } if ((biome & 8) > 0) { return (Biome)8; } if ((biome & 1) > 0) { return (Biome)1; } if ((biome & 0x100) > 0) { return (Biome)256; } return (Biome)0; } } public class BossInfo { public Biome Biome { get; } public string BossName { get; } public string GlobalKey { get; } public int Tier { get; } public BossInfo(Biome biome, string bossName, string globalKey, int tier) { //IL_0009: Unknown result type (might be due to invalid IL or missing references) //IL_000a: Unknown result type (might be due to invalid IL or missing references) Biome = biome; BossName = bossName; GlobalKey = globalKey; Tier = tier; } } public static class BossReliefManager { public static readonly List<BossInfo> Bosses; private static readonly Dictionary<Biome, BossInfo> _biomeBossLookup; static BossReliefManager() { //IL_00e0: Unknown result type (might be due to invalid IL or missing references) Bosses = new List<BossInfo> { new BossInfo((Biome)1, "Eikthyr", "defeated_eikthyr", 1), new BossInfo((Biome)8, "The Elder", "defeated_gdking", 2), new BossInfo((Biome)2, "Bonemass", "defeated_bonemass", 3), new BossInfo((Biome)4, "Moder", "defeated_dragon", 4), new BossInfo((Biome)16, "Yagluth", "defeated_goblinking", 5), new BossInfo((Biome)512, "The Queen", "defeated_queen", 6), new BossInfo((Biome)32, "Fader", "defeated_fader", 7) }; _biomeBossLookup = new Dictionary<Biome, BossInfo>(); foreach (BossInfo boss in Bosses) { _biomeBossLookup[boss.Biome] = boss; } } public static BossInfo GetBossForBiome(Biome biome) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_000e: Unknown result type (might be due to invalid IL or missing references) biome = BiomeManager.GetPrimaryBiome(biome); if (_biomeBossLookup.TryGetValue(biome, out var value)) { return value; } return null; } public static bool IsBossDefeated(string globalKey) { if (string.IsNullOrEmpty(globalKey) || (Object)(object)ZoneSystem.instance == (Object)null) { return false; } return ZoneSystem.instance.GetGlobalKey(globalKey); } public static int GetTotalDefeatedBossCount() { int num = 0; for (int i = 0; i < Bosses.Count; i++) { if (IsBossDefeated(Bosses[i].GlobalKey)) { num++; } } return num; } public static float GetReliefFraction(Biome biome) { //IL_0020: Unknown result type (might be due to invalid IL or missing references) //IL_0021: Unknown result type (might be due to invalid IL or missing references) //IL_0026: Unknown result type (might be due to invalid IL or missing references) //IL_0028: Unknown result type (might be due to invalid IL or missing references) //IL_002e: Invalid comparison between Unknown and I4 //IL_00c6: Unknown result type (might be due to invalid IL or missing references) if (!TrollTollPlugin.BossReliefEnabled.Value) { return 0f; } biome = BiomeManager.GetPrimaryBiome(biome); if ((int)biome == 256) { if (!TrollTollPlugin.OceanBossRelief.Value) { return 0f; } int totalDefeatedBossCount = GetTotalDefeatedBossCount(); if (totalDefeatedBossCount <= 0) { return 0f; } float value = TrollTollPlugin.BossReliefBasePercent.Value; float value2 = TrollTollPlugin.BossReliefPerAdditionalBossPercent.Value; float value3 = TrollTollPlugin.BossReliefCapPercent.Value; float num = value + (float)(totalDefeatedBossCount - 1) * value2; return Mathf.Clamp(num, 0f, value3) / 100f; } BossInfo bossForBiome = GetBossForBiome(biome); if (bossForBiome == null) { return 0f; } if (!IsBossDefeated(bossForBiome.GlobalKey)) { return 0f; } float value4 = TrollTollPlugin.BossReliefBasePercent.Value; float value5 = TrollTollPlugin.BossReliefPerAdditionalBossPercent.Value; float value6 = TrollTollPlugin.BossReliefCapPercent.Value; int num2 = 0; string value7 = TrollTollPlugin.BossReliefCalculationMode.Value; if (value7 == "AllDefeatedBosses") { for (int i = 0; i < Bosses.Count; i++) { if (Bosses[i] != bossForBiome && IsBossDefeated(Bosses[i].GlobalKey)) { num2++; } } } else { for (int j = 0; j < Bosses.Count; j++) { if (Bosses[j].Tier > bossForBiome.Tier && IsBossDefeated(Bosses[j].GlobalKey)) { num2++; } } } float num3 = value4 + (float)num2 * value5; float num4 = Mathf.Clamp(num3, 0f, value6); return num4 / 100f; } public static string GetStatusSummary(Biome biome) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_0009: Unknown result type (might be due to invalid IL or missing references) //IL_0010: Unknown result type (might be due to invalid IL or missing references) //IL_0067: 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) biome = BiomeManager.GetPrimaryBiome(biome); BossInfo bossForBiome = GetBossForBiome(biome); float num = GetReliefFraction(biome) * 100f; int totalDefeatedBossCount = GetTotalDefeatedBossCount(); if (bossForBiome == null) { return $"Biome: {biome} | Relief: {num:0}% | World Bosses Defeated: {totalDefeatedBossCount}"; } bool flag = IsBossDefeated(bossForBiome.GlobalKey); return $"Biome: {biome} | Boss: {bossForBiome.BossName} (Defeated: {flag}) | Relief: {num:0}% | World Bosses Defeated: {totalDefeatedBossCount}"; } } public static class CreatureManager { public static ConfigEntry<string> IgnoredCreatures; public static ConfigEntry<string> EnforcedThreatCreatures; public static ConfigEntry<string> CustomCreatureRadii; public static ConfigEntry<bool> BossesAlwaysThreat; public static ConfigEntry<bool> IgnoreBoars; public static ConfigEntry<bool> IgnoreNecks; public static ConfigEntry<bool> IgnoreGreylings; public static ConfigEntry<bool> IgnoreBats; public static ConfigEntry<bool> IgnoreSkeletons; private static readonly HashSet<string> _parsedIgnored = new HashSet<string>(StringComparer.OrdinalIgnoreCase); private static readonly HashSet<string> _parsedEnforced = new HashSet<string>(StringComparer.OrdinalIgnoreCase); private static readonly Dictionary<string, float> _parsedCustomRadii = new Dictionary<string, float>(StringComparer.OrdinalIgnoreCase); public static float MaxCustomRadius { get; private set; } = 30f; public static void Initialize(ConfigFile config) { IgnoredCreatures = config.Bind<string>("6 - Creature Controls", "IgnoredCreatures", "Deer, Seagull, Crow", "Comma-separated list of creature names or prefabs that NEVER trigger threat or consume combat stamina (e.g. 'Greyling, Neck, Boar, Skeleton'). Matches display name or prefab name."); EnforcedThreatCreatures = config.Bind<string>("6 - Creature Controls", "EnforcedThreatCreatures", "Troll, Abomination, StoneGolem, Gjall, SeekerSoldier", "Comma-separated list of creature names or prefabs that ALWAYS trigger threat when within range, regardless of alert status (e.g. 'Troll, Abomination, Gjall')."); CustomCreatureRadii = config.Bind<string>("6 - Creature Controls", "CustomCreatureRadii", "Troll:50, Abomination:40, Gjall:50", "Creature-specific threat detection radius in 'CreatureName:Distance' format (e.g. 'Troll:50, Greyling:10'). Overrides biome and global detection radius."); BossesAlwaysThreat = config.Bind<bool>("6 - Creature Controls", "BossesAlwaysThreat", true, "If true, any active boss in detection range is always treated as an active threat."); IgnoreBoars = config.Bind<bool>("6 - Creature Controls", "IgnoreBoars", false, "Quick toggle: If true, wild Boars never trigger threat or interrupt free stamina."); IgnoreNecks = config.Bind<bool>("6 - Creature Controls", "IgnoreNecks", false, "Quick toggle: If true, Necks never trigger threat or interrupt free stamina."); IgnoreGreylings = config.Bind<bool>("6 - Creature Controls", "IgnoreGreylings", false, "Quick toggle: If true, Greylings never trigger threat or interrupt free stamina."); IgnoreBats = config.Bind<bool>("6 - Creature Controls", "IgnoreBats", false, "Quick toggle: If true, Bats never trigger threat or interrupt free stamina."); IgnoreSkeletons = config.Bind<bool>("6 - Creature Controls", "IgnoreSkeletons", false, "Quick toggle: If true, Skeletons never trigger threat or interrupt free stamina."); IgnoredCreatures.SettingChanged += delegate { RefreshCache(); }; EnforcedThreatCreatures.SettingChanged += delegate { RefreshCache(); }; CustomCreatureRadii.SettingChanged += delegate { RefreshCache(); }; RefreshCache(); } public static void RefreshCache() { _parsedIgnored.Clear(); if (!string.IsNullOrWhiteSpace(IgnoredCreatures.Value)) { string[] array = IgnoredCreatures.Value.Split(new char[2] { ',', ';' }, StringSplitOptions.RemoveEmptyEntries); string[] array2 = array; foreach (string input in array2) { string text = CleanName(input); if (!string.IsNullOrEmpty(text)) { _parsedIgnored.Add(text); } } } _parsedEnforced.Clear(); if (!string.IsNullOrWhiteSpace(EnforcedThreatCreatures.Value)) { string[] array3 = EnforcedThreatCreatures.Value.Split(new char[2] { ',', ';' }, StringSplitOptions.RemoveEmptyEntries); string[] array4 = array3; foreach (string input2 in array4) { string text2 = CleanName(input2); if (!string.IsNullOrEmpty(text2)) { _parsedEnforced.Add(text2); } } } _parsedCustomRadii.Clear(); float num = 30f; if (!string.IsNullOrWhiteSpace(CustomCreatureRadii.Value)) { string[] array5 = CustomCreatureRadii.Value.Split(new char[2] { ',', ';' }, StringSplitOptions.RemoveEmptyEntries); string[] array6 = array5; foreach (string text3 in array6) { string[] array7 = text3.Split(':'); if (array7.Length != 2 || !float.TryParse(array7[1].Trim(), NumberStyles.Float, CultureInfo.InvariantCulture, out var result)) { continue; } string text4 = CleanName(array7[0]); if (!string.IsNullOrEmpty(text4)) { _parsedCustomRadii[text4] = result; if (result > num) { num = result; } } } } MaxCustomRadius = num; } public static string CleanName(string input) { if (string.IsNullOrEmpty(input)) { return string.Empty; } StringBuilder stringBuilder = new StringBuilder(input.Length); foreach (char c in input) { if (char.IsLetterOrDigit(c)) { stringBuilder.Append(char.ToLowerInvariant(c)); } } return stringBuilder.ToString(); } public static List<string> GetCandidateNames(Character character) { List<string> list = new List<string>(4); if ((Object)(object)character == (Object)null) { return list; } if ((Object)(object)((Component)character).gameObject != (Object)null) { string prefabName = Utils.GetPrefabName(((Component)character).gameObject); if (!string.IsNullOrEmpty(prefabName)) { list.Add(CleanName(prefabName)); } } try { string hoverName = character.GetHoverName(); if (!string.IsNullOrEmpty(hoverName)) { list.Add(CleanName(hoverName)); } } catch { } if (!string.IsNullOrEmpty(character.m_name)) { string text = CleanName(character.m_name); list.Add(text); if (text.StartsWith("enemy")) { list.Add(text.Substring(5)); } } if ((Object)(object)((Component)character).gameObject != (Object)null) { string input = ((Object)((Component)character).gameObject).name.Replace("(Clone)", ""); list.Add(CleanName(input)); } return list; } public static bool IsCreatureIgnored(Character character) { if ((Object)(object)character == (Object)null) { return false; } List<string> candidateNames = GetCandidateNames(character); if (IgnoreBoars.Value && candidateNames.Contains("boar")) { return true; } if (IgnoreNecks.Value && candidateNames.Contains("neck")) { return true; } if (IgnoreGreylings.Value && candidateNames.Contains("greyling")) { return true; } if (IgnoreBats.Value && candidateNames.Contains("bat")) { return true; } if (IgnoreSkeletons.Value && candidateNames.Contains("skeleton")) { return true; } for (int i = 0; i < candidateNames.Count; i++) { if (_parsedIgnored.Contains(candidateNames[i])) { return true; } } return false; } public static bool IsCreatureEnforcedThreat(Character character) { if ((Object)(object)character == (Object)null) { return false; } if (BossesAlwaysThreat.Value && character.IsBoss()) { return true; } List<string> candidateNames = GetCandidateNames(character); for (int i = 0; i < candidateNames.Count; i++) { if (_parsedEnforced.Contains(candidateNames[i])) { return true; } } return false; } public static float GetCustomRadius(Character character, float defaultRadius) { if ((Object)(object)character == (Object)null) { return defaultRadius; } List<string> candidateNames = GetCandidateNames(character); for (int i = 0; i < candidateNames.Count; i++) { if (_parsedCustomRadii.TryGetValue(candidateNames[i], out var value)) { return value; } } return defaultRadius; } } public static class EnemyDetector { private static float _lastScanTime = -10f; private static bool _cachedHasThreat = false; private static Vector3 _lastPlayerPosition = Vector3.zero; public static float GetStaminaDiscount(Player player) { //IL_008c: Unknown result type (might be due to invalid IL or missing references) //IL_0091: Unknown result type (might be due to invalid IL or missing references) //IL_00c2: Unknown result type (might be due to invalid IL or missing references) //IL_00a4: Unknown result type (might be due to invalid IL or missing references) //IL_00b9: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)player == (Object)null || (Object)(object)player != (Object)(object)Player.m_localPlayer) { return 0f; } if (!TrollTollPlugin.ModEnabled.Value) { return 0f; } if (!TrollTollPlugin.AllowSwimmingStaminaDiscount.Value && ((Character)player).IsSwimming()) { return 0f; } if (!TrollTollPlugin.AllowEncumberedStaminaDiscount.Value && ((Character)player).IsEncumbered()) { return 0f; } Biome currentBiome = player.GetCurrentBiome(); if (!HasThreatNearby(player)) { if (BiomeManager.IsBiomeModEnabled(currentBiome)) { return 1f; } return BossReliefManager.GetReliefFraction(currentBiome); } return BossReliefManager.GetReliefFraction(currentBiome); } public static bool IsStaminaDiscountActive(Player player) { return GetStaminaDiscount(player) >= 1f; } public static bool HasThreatNearby(Player player) { //IL_001c: Unknown result type (might be due to invalid IL or missing references) //IL_0021: Unknown result type (might be due to invalid IL or missing references) //IL_0035: Unknown result type (might be due to invalid IL or missing references) //IL_0036: Unknown result type (might be due to invalid IL or missing references) //IL_003b: Unknown result type (might be due to invalid IL or missing references) //IL_0040: Unknown result type (might be due to invalid IL or missing references) //IL_0068: Unknown result type (might be due to invalid IL or missing references) //IL_0069: Unknown result type (might be due to invalid IL or missing references) //IL_006f: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)player == (Object)null) { return false; } float time = Time.time; Vector3 position = ((Component)player).transform.position; if (time - _lastScanTime < TrollTollPlugin.UpdateInterval.Value) { Vector3 val = position - _lastPlayerPosition; if (((Vector3)(ref val)).sqrMagnitude < 9f) { return _cachedHasThreat; } } _lastScanTime = time; _lastPlayerPosition = position; _cachedHasThreat = ScanForThreats(player, position); return _cachedHasThreat; } private static bool ScanForThreats(Player player, Vector3 playerPosition) { //IL_0025: Unknown result type (might be due to invalid IL or missing references) //IL_002a: Unknown result type (might be due to invalid IL or missing references) //IL_002b: Unknown result type (might be due to invalid IL or missing references) //IL_005b: Unknown result type (might be due to invalid IL or missing references) //IL_00b6: 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_00bc: Unknown result type (might be due to invalid IL or missing references) //IL_00c1: Unknown result type (might be due to invalid IL or missing references) if (TrollTollPlugin.EnforceDuringRaids.Value && RandEventSystem.HaveActiveEvent()) { return true; } Biome currentBiome = player.GetCurrentBiome(); if (!BiomeManager.IsBiomeModEnabled(currentBiome)) { return true; } List<Character> allCharacters = Character.GetAllCharacters(); if (allCharacters == null) { return false; } float biomeDetectionRadius = BiomeManager.GetBiomeDetectionRadius(currentBiome); float num = Mathf.Max(biomeDetectionRadius, CreatureManager.MaxCustomRadius); float num2 = num * num; for (int i = 0; i < allCharacters.Count; i++) { Character val = allCharacters[i]; if ((Object)(object)val == (Object)null || (Object)(object)val == (Object)(object)player || val.IsDead()) { continue; } Vector3 val2 = ((Component)val).transform.position - playerPosition; float sqrMagnitude = ((Vector3)(ref val2)).sqrMagnitude; if (sqrMagnitude > num2) { continue; } if (val.IsPlayer()) { if (TrollTollPlugin.EnableInPVP.Value) { Player val3 = (Player)(object)((val is Player) ? val : null); if ((Object)(object)val3 != (Object)null && ((Character)player).IsPVPEnabled() && ((Character)val3).IsPVPEnabled() && sqrMagnitude <= biomeDetectionRadius * biomeDetectionRadius) { return true; } } } else { if (CreatureManager.IsCreatureIgnored(val)) { continue; } float customRadius = CreatureManager.GetCustomRadius(val, biomeDetectionRadius); if (sqrMagnitude > customRadius * customRadius) { continue; } if (CreatureManager.IsCreatureEnforcedThreat(val)) { return true; } if (TrollTollPlugin.IncludePassiveCreatures.Value) { return true; } if (BaseAI.IsEnemy((Character)(object)player, val)) { if (!TrollTollPlugin.RequireAlertedOnly.Value) { return true; } BaseAI baseAI = val.GetBaseAI(); if ((Object)(object)baseAI != (Object)null && (baseAI.IsAlerted() || baseAI.HaveTarget())) { return true; } } else { BaseAI baseAI2 = val.GetBaseAI(); if ((Object)(object)baseAI2 != (Object)null && (baseAI2.IsAlerted() || baseAI2.HaveTarget()) && !val.IsTamed()) { return true; } } } } return false; } } [BepInPlugin("com.beardymike.trolltoll", "Troll-Toll", "0.20.4")] public class TrollTollPlugin : BaseUnityPlugin { public const string PluginGUID = "com.beardymike.trolltoll"; public const string PluginName = "Troll-Toll"; public const string PluginVersion = "0.20.4"; public const string PluginAuthor = "BeardyMike"; public static ConfigEntry<bool> ModEnabled; public static ConfigEntry<float> DetectionRadius; public static ConfigEntry<bool> IncludePassiveCreatures; public static ConfigEntry<bool> RequireAlertedOnly; public static ConfigEntry<bool> EnforceDuringRaids; public static ConfigEntry<bool> EnableInPVP; public static ConfigEntry<bool> AllowSwimmingStaminaDiscount; public static ConfigEntry<bool> AllowEncumberedStaminaDiscount; public static ConfigEntry<float> UpdateInterval; public static ConfigEntry<bool> BossReliefEnabled; public static ConfigEntry<float> BossReliefBasePercent; public static ConfigEntry<float> BossReliefPerAdditionalBossPercent; public static ConfigEntry<float> BossReliefCapPercent; public static ConfigEntry<string> BossReliefCalculationMode; public static ConfigEntry<bool> OceanBossRelief; public static ConfigEntry<bool> ShowBiomeEntryMessage; private Harmony _harmony; private void Awake() { //IL_018f: Unknown result type (might be due to invalid IL or missing references) //IL_0199: Expected O, but got Unknown //IL_01cc: Unknown result type (might be due to invalid IL or missing references) //IL_01d6: Expected O, but got Unknown //IL_0209: Unknown result type (might be due to invalid IL or missing references) //IL_0213: Expected O, but got Unknown //IL_0282: Unknown result type (might be due to invalid IL or missing references) //IL_028c: Expected O, but got Unknown ModEnabled = ((BaseUnityPlugin)this).Config.Bind<bool>("1 - General", "Enabled", true, "Master switch to enable or disable Troll-Toll."); DetectionRadius = ((BaseUnityPlugin)this).Config.Bind<float>("1 - General", "DetectionRadius", 30f, "Default distance in meters to search for nearby threats. Inside this radius, stamina costs apply normally unless reduced by Boss Relief."); IncludePassiveCreatures = ((BaseUnityPlugin)this).Config.Bind<bool>("2 - Detection", "IncludePassiveCreatures", false, "If true, peaceful wildlife (like deer and birds) also triggers normal stamina usage."); RequireAlertedOnly = ((BaseUnityPlugin)this).Config.Bind<bool>("2 - Detection", "RequireAlertedOnly", false, "If true, nearby enemies only trigger stamina consumption once they are actively alerted (spotted you) or targeting someone."); EnforceDuringRaids = ((BaseUnityPlugin)this).Config.Bind<bool>("2 - Detection", "EnforceDuringRaids", true, "If true, stamina is always consumed normally whenever a base raid / random event is in progress."); EnableInPVP = ((BaseUnityPlugin)this).Config.Bind<bool>("2 - Detection", "EnableInPVP", true, "If true, other players with PvP enabled in range will trigger normal stamina costs."); AllowSwimmingStaminaDiscount = ((BaseUnityPlugin)this).Config.Bind<bool>("3 - Restrictions", "AllowSwimmingStaminaDiscount", false, "If true, swimming out of combat will consume zero stamina. Keep false for vanilla ocean balance."); AllowEncumberedStaminaDiscount = ((BaseUnityPlugin)this).Config.Bind<bool>("3 - Restrictions", "AllowEncumberedStaminaDiscount", false, "If true, walking while encumbered out of combat will consume zero stamina."); UpdateInterval = ((BaseUnityPlugin)this).Config.Bind<float>("4 - Performance", "UpdateInterval", 0.2f, "How often (in seconds) the mod scans for nearby creatures. Caching scans prevents frame rate drops."); BiomeManager.Initialize(((BaseUnityPlugin)this).Config); CreatureManager.Initialize(((BaseUnityPlugin)this).Config); BossReliefEnabled = ((BaseUnityPlugin)this).Config.Bind<bool>("7 - Boss Relief System", "BossReliefEnabled", true, "Defeating the boss of a biome grants permanent stamina cost relief in that biome, scaling stronger as additional bosses are defeated."); BossReliefBasePercent = ((BaseUnityPlugin)this).Config.Bind<float>("7 - Boss Relief System", "BossReliefBasePercent", 25f, new ConfigDescription("Base stamina relief percentage (stamina cost discount) granted in a biome immediately upon defeating its boss. (Recommended: 25% - 75%).", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 100f), Array.Empty<object>())); BossReliefPerAdditionalBossPercent = ((BaseUnityPlugin)this).Config.Bind<float>("7 - Boss Relief System", "BossReliefPerAdditionalBossPercent", 25f, new ConfigDescription("Additional stamina relief percentage granted in a conquered biome for each additional boss defeated beyond that biome. E.g. at 25% base + 25% per additional, 4 bosses total grants 100% relief in Meadows.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 100f), Array.Empty<object>())); BossReliefCapPercent = ((BaseUnityPlugin)this).Config.Bind<float>("7 - Boss Relief System", "BossReliefCapPercent", 100f, new ConfigDescription("Maximum stamina relief percentage attainable in any biome (100% = zero stamina consumed even during combat).", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 100f), Array.Empty<object>())); BossReliefCalculationMode = ((BaseUnityPlugin)this).Config.Bind<string>("7 - Boss Relief System", "BossReliefCalculationMode", "ProgressionTier", "ProgressionTier: Only bosses defeated in higher-tier biomes grant additional relief (e.g. Elder/Bonemass/Moder increase Meadows relief). AllDefeatedBosses: Any other defeated boss in the world grants additional relief."); OceanBossRelief = ((BaseUnityPlugin)this).Config.Bind<bool>("7 - Boss Relief System", "OceanBossRelief", true, "If true, Ocean biome receives stamina relief scaling with total world bosses defeated."); ShowBiomeEntryMessage = ((BaseUnityPlugin)this).Config.Bind<bool>("7 - Boss Relief System", "ShowBiomeEntryMessage", false, "If true, displays a brief top-left notification showing your current biome's Boss Relief stamina discount upon entering a new biome."); _harmony = new Harmony("com.beardymike.trolltoll"); _harmony.PatchAll(); ((BaseUnityPlugin)this).Logger.LogInfo((object)"Troll-Toll v0.20.4 by BeardyMike loaded successfully."); } private void OnDestroy() { Harmony harmony = _harmony; if (harmony != null) { harmony.UnpatchSelf(); } } } } namespace TrollToll.Patches { [HarmonyPatch(typeof(Player), "UseStamina")] public static class Player_UseStamina_Patch { [HarmonyPrefix] public static bool Prefix(Player __instance, ref float v) { if (v <= 0f) { return true; } if ((Object)(object)__instance != (Object)(object)Player.m_localPlayer) { return true; } float staminaDiscount = EnemyDetector.GetStaminaDiscount(__instance); if (staminaDiscount >= 1f) { return false; } if (staminaDiscount > 0f) { v *= 1f - staminaDiscount; return true; } return true; } } [HarmonyPatch(typeof(Player), "HaveStamina")] public static class Player_HaveStamina_Patch { [HarmonyPrefix] public static bool Prefix(Player __instance, ref bool __result, float amount) { if ((Object)(object)__instance != (Object)(object)Player.m_localPlayer) { return true; } float staminaDiscount = EnemyDetector.GetStaminaDiscount(__instance); if (staminaDiscount >= 1f) { __result = true; return false; } if (staminaDiscount > 0f) { float num = amount * (1f - staminaDiscount); if (__instance.GetStamina() >= num) { __result = true; return false; } } return true; } } [HarmonyPatch(typeof(Chat), "SendText")] public static class Chat_SendText_Patch { [HarmonyPrefix] public static bool Prefix(Chat __instance, Type type, string text) { //IL_0055: Unknown result type (might be due to invalid IL or missing references) //IL_005a: 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 (string.IsNullOrEmpty(text)) { return true; } string text2 = text.Trim(); if (text2.Equals("/trolltoll", StringComparison.OrdinalIgnoreCase) || text2.Equals("/tt", StringComparison.OrdinalIgnoreCase)) { if ((Object)(object)Player.m_localPlayer != (Object)null) { Biome currentBiome = Player.m_localPlayer.GetCurrentBiome(); string statusSummary = BossReliefManager.GetStatusSummary(currentBiome); float num = EnemyDetector.GetStaminaDiscount(Player.m_localPlayer) * 100f; bool flag = EnemyDetector.HasThreatNearby(Player.m_localPlayer); string text3 = $"<color=orange>[Troll-Toll]</color> {statusSummary}\nThreat Nearby: {flag} | Active Stamina Discount: {num:0}%"; ((Terminal)Chat.instance).AddString(text3); } else { ((Terminal)Chat.instance).AddString("<color=orange>[Troll-Toll]</color> Local player not active."); } return false; } return true; } } [HarmonyPatch(typeof(Player), "UpdateBiome")] public static class Player_UpdateBiome_Patch { private static Biome _previousBiome; [HarmonyPostfix] public static void Postfix(Player __instance, float dt) { //IL_003b: Unknown result type (might be due to invalid IL or missing references) //IL_0040: Unknown result type (might be due to invalid IL or missing references) //IL_0041: Unknown result type (might be due to invalid IL or missing references) //IL_0042: Unknown result type (might be due to invalid IL or missing references) //IL_0051: Unknown result type (might be due to invalid IL or missing references) //IL_0052: Unknown result type (might be due to invalid IL or missing references) //IL_0057: Unknown result type (might be due to invalid IL or missing references) //IL_006f: Unknown result type (might be due to invalid IL or missing references) //IL_009d: Unknown result type (might be due to invalid IL or missing references) //IL_008b: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)__instance != (Object)(object)Player.m_localPlayer || !TrollTollPlugin.ShowBiomeEntryMessage.Value || !TrollTollPlugin.ModEnabled.Value) { return; } Biome currentBiome = __instance.GetCurrentBiome(); if (currentBiome != _previousBiome) { _previousBiome = currentBiome; if ((int)currentBiome != 0 && (Object)(object)MessageHud.instance != (Object)null) { float num = BossReliefManager.GetReliefFraction(currentBiome) * 100f; string text = ((num > 0f) ? $"{currentBiome}: {num:0}% Boss Stamina Relief" : $"{currentBiome}: 0% Boss Relief"); MessageHud.instance.ShowMessage((MessageType)1, text, 0, (Sprite)null, false, true); } } } } }