Please disclose if your mod was created primarily 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 BetterTrinkets v1.0.0
plugins/BetterTrinkets.dll
Decompiled a month agousing System; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Security; using System.Security.Permissions; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; 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: IgnoresAccessChecksTo("assembly_valheim")] [assembly: TargetFramework(".NETFramework,Version=v4.6.2", FrameworkDisplayName = ".NET Framework 4.6.2")] [assembly: AssemblyCompany("BetterTrinkets")] [assembly: AssemblyConfiguration("Debug")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: AssemblyInformationalVersion("1.0.0")] [assembly: AssemblyProduct("BetterTrinkets")] [assembly: AssemblyTitle("BetterTrinkets")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("1.0.0.0")] [module: UnverifiableCode] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Microsoft.CodeAnalysis.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 BetterTrinkets { public static class ModConfig { public static ConfigFile TrinketConfig; public static ConfigEntry<bool> Enabled; public static ConfigEntry<bool> DoublingOnAdrenaline; public static ConfigEntry<bool> DebugLogging; public static ConfigEntry<float> HotF_PassiveHealthRegen; public static ConfigEntry<float> HotF_DoubledHealthRegen; public static ConfigEntry<float> HotF_Duration; public static ConfigEntry<float> BP_PassiveStaminaRegen; public static ConfigEntry<float> BP_DoubledStaminaRegen; public static ConfigEntry<float> BP_Duration; public static ConfigEntry<float> IB_PassiveInstantHealth; public static ConfigEntry<float> IB_PassiveArmor; public static ConfigEntry<float> IB_DoubledInstantHealth; public static ConfigEntry<float> IB_DoubledArmor; public static ConfigEntry<float> IB_Duration; public static ConfigEntry<float> NA_PassiveInstantStamina; public static ConfigEntry<float> NA_PassiveSpeed; public static ConfigEntry<float> NA_DoubledInstantStamina; public static ConfigEntry<float> NA_DoubledSpeed; public static ConfigEntry<float> NA_Duration; public static ConfigEntry<float> FoD_PassiveSwimSpeed; public static ConfigEntry<float> FoD_PassiveSwimStaminaReduction; public static ConfigEntry<float> FoD_DoubledSwimSpeed; public static ConfigEntry<float> FoD_DoubledSwimStaminaReduction; public static ConfigEntry<float> FoD_Duration; public static ConfigEntry<float> WS_PassiveBowSkill; public static ConfigEntry<float> WS_PassiveSneakSkill; public static ConfigEntry<float> WS_PassivePierceDamage; public static ConfigEntry<float> WS_DoubledBowSkill; public static ConfigEntry<float> WS_DoubledSneakSkill; public static ConfigEntry<float> WS_DoubledPierceDamage; public static ConfigEntry<float> WS_Duration; public static ConfigEntry<string> CH_PassiveResistance; public static ConfigEntry<string> CH_DoubledResistance; public static ConfigEntry<float> CH_Duration; public static ConfigEntry<float> BotB_PassiveInstantHealth; public static ConfigEntry<float> BotB_PassiveClubSkill; public static ConfigEntry<float> BotB_PassiveBluntDamage; public static ConfigEntry<float> BotB_DoubledInstantHealth; public static ConfigEntry<float> BotB_DoubledClubSkill; public static ConfigEntry<float> BotB_DoubledBluntDamage; public static ConfigEntry<float> BotB_Duration; public static ConfigEntry<float> EM_PassiveBlockStaminaReduction; public static ConfigEntry<float> EM_PassiveParryBonus; public static ConfigEntry<float> EM_PassiveDodgeSkill; public static ConfigEntry<float> EM_DoubledBlockStaminaReduction; public static ConfigEntry<float> EM_DoubledParryBonus; public static ConfigEntry<float> EM_DoubledDodgeSkill; public static ConfigEntry<float> EM_Duration; public static ConfigEntry<float> PE_PassiveEitrRegen; public static ConfigEntry<float> PE_DoubledEitrRegen; public static ConfigEntry<float> PE_Duration; public static ConfigEntry<float> RS_PassiveInstantStamina; public static ConfigEntry<float> RS_PassiveSlashDamage; public static ConfigEntry<float> RS_DoubledInstantStamina; public static ConfigEntry<float> RS_DoubledSlashDamage; public static ConfigEntry<float> RS_Duration; public static ConfigEntry<float> J_PassiveInstantEitr; public static ConfigEntry<float> J_PassiveElementalMagicSkill; public static ConfigEntry<float> J_PassiveBloodMagicSkill; public static ConfigEntry<float> J_DoubledInstantEitr; public static ConfigEntry<float> J_DoubledElementalMagicSkill; public static ConfigEntry<float> J_DoubledBloodMagicSkill; public static ConfigEntry<float> J_Duration; public static ConfigEntry<float> B_PassiveInstantHealth; public static ConfigEntry<float> B_PassiveInstantStamina; public static ConfigEntry<float> B_DoubledInstantHealth; public static ConfigEntry<float> B_DoubledInstantStamina; public static ConfigEntry<float> B_Duration; public static void Init() { //IL_0011: Unknown result type (might be due to invalid IL or missing references) //IL_001b: Expected O, but got Unknown TrinketConfig = new ConfigFile(Path.Combine(Paths.ConfigPath, "better_trinkets.cfg"), true); Enabled = TrinketConfig.Bind<bool>("General", "Enabled", true, "Enable or disable the entire mod."); DoublingOnAdrenaline = TrinketConfig.Bind<bool>("General", "DoublingOnAdrenaline", true, "When adrenaline fills, double the trinket effect for its default duration."); DebugLogging = TrinketConfig.Bind<bool>("General", "DebugLogging", false, "Enable verbose logging to BepInEx log for troubleshooting."); HotF_PassiveHealthRegen = TrinketConfig.Bind<float>("Heart of the Forest", "PassiveHealthRegen", 1.15f, "Health regen multiplier while passively active (1.0 = no bonus, 1.15 = +15%)."); HotF_DoubledHealthRegen = TrinketConfig.Bind<float>("Heart of the Forest", "DoubledHealthRegen", 1.3f, "Health regen multiplier during adrenaline doubling."); HotF_Duration = TrinketConfig.Bind<float>("Heart of the Forest", "Duration", 60f, "Duration in seconds for the doubled effect."); BP_PassiveStaminaRegen = TrinketConfig.Bind<float>("Bronze Pendant", "PassiveStaminaRegen", 1.15f, "Stamina regen multiplier while passively active (1.0 = no bonus, 1.15 = +15%)."); BP_DoubledStaminaRegen = TrinketConfig.Bind<float>("Bronze Pendant", "DoubledStaminaRegen", 1.3f, "Stamina regen multiplier during adrenaline doubling."); BP_Duration = TrinketConfig.Bind<float>("Bronze Pendant", "Duration", 60f, "Duration in seconds for the doubled effect."); IB_PassiveInstantHealth = TrinketConfig.Bind<float>("Iron Brooch", "PassiveInstantHealth", 25f, "One-time health gain when trinket is equipped."); IB_PassiveArmor = TrinketConfig.Bind<float>("Iron Brooch", "PassiveArmor", 10f, "Bonus armor while passively active."); IB_DoubledInstantHealth = TrinketConfig.Bind<float>("Iron Brooch", "DoubledInstantHealth", 50f, "Health gain when adrenaline doubling activates."); IB_DoubledArmor = TrinketConfig.Bind<float>("Iron Brooch", "DoubledArmor", 20f, "Bonus armor during adrenaline doubling."); IB_Duration = TrinketConfig.Bind<float>("Iron Brooch", "Duration", 30f, "Duration in seconds for the doubled effect."); NA_PassiveInstantStamina = TrinketConfig.Bind<float>("Nimble Anklet", "PassiveInstantStamina", 25f, "One-time stamina gain when trinket is equipped."); NA_PassiveSpeed = TrinketConfig.Bind<float>("Nimble Anklet", "PassiveSpeed", 0.1f, "Movement speed modifier while passively active (0.10 = +10%)."); NA_DoubledInstantStamina = TrinketConfig.Bind<float>("Nimble Anklet", "DoubledInstantStamina", 50f, "Stamina gain when adrenaline doubling activates."); NA_DoubledSpeed = TrinketConfig.Bind<float>("Nimble Anklet", "DoubledSpeed", 0.2f, "Movement speed modifier during adrenaline doubling."); NA_Duration = TrinketConfig.Bind<float>("Nimble Anklet", "Duration", 30f, "Duration in seconds for the doubled effect."); FoD_PassiveSwimSpeed = TrinketConfig.Bind<float>("Fins of Destiny", "PassiveSwimSpeed", 0.5f, "Swim speed modifier while passively active (0.50 = +50%)."); FoD_PassiveSwimStaminaReduction = TrinketConfig.Bind<float>("Fins of Destiny", "PassiveSwimStaminaReduction", 0.8f, "Swim stamina usage reduction (0.80 = -80% stamina cost)."); FoD_DoubledSwimSpeed = TrinketConfig.Bind<float>("Fins of Destiny", "DoubledSwimSpeed", 1f, "Swim speed modifier during adrenaline doubling."); FoD_DoubledSwimStaminaReduction = TrinketConfig.Bind<float>("Fins of Destiny", "DoubledSwimStaminaReduction", 0.8f, "Swim stamina usage reduction during doubling."); FoD_Duration = TrinketConfig.Bind<float>("Fins of Destiny", "Duration", 120f, "Duration in seconds for the doubled effect."); WS_PassiveBowSkill = TrinketConfig.Bind<float>("Wolf Sight", "PassiveBowSkill", 20f, "Bow skill bonus while passively active."); WS_PassiveSneakSkill = TrinketConfig.Bind<float>("Wolf Sight", "PassiveSneakSkill", 20f, "Sneak skill bonus while passively active."); WS_PassivePierceDamage = TrinketConfig.Bind<float>("Wolf Sight", "PassivePierceDamage", 0.15f, "Pierce damage modifier while passively active (0.15 = +15%)."); WS_DoubledBowSkill = TrinketConfig.Bind<float>("Wolf Sight", "DoubledBowSkill", 40f, "Bow skill bonus during adrenaline doubling."); WS_DoubledSneakSkill = TrinketConfig.Bind<float>("Wolf Sight", "DoubledSneakSkill", 40f, "Sneak skill bonus during adrenaline doubling."); WS_DoubledPierceDamage = TrinketConfig.Bind<float>("Wolf Sight", "DoubledPierceDamage", 0.3f, "Pierce damage modifier during adrenaline doubling."); WS_Duration = TrinketConfig.Bind<float>("Wolf Sight", "Duration", 30f, "Duration in seconds for the doubled effect."); CH_PassiveResistance = TrinketConfig.Bind<string>("Crystal Heart", "PassiveResistance", "Resistant", "Damage resistance level while passively active (Normal, Resistant, VeryResistant, Immune)."); CH_DoubledResistance = TrinketConfig.Bind<string>("Crystal Heart", "DoubledResistance", "VeryResistant", "Damage resistance level during adrenaline doubling."); CH_Duration = TrinketConfig.Bind<float>("Crystal Heart", "Duration", 50f, "Duration in seconds for the doubled effect."); BotB_PassiveInstantHealth = TrinketConfig.Bind<float>("Bracelets of the Brave", "PassiveInstantHealth", 50f, "One-time health gain when trinket is equipped."); BotB_PassiveClubSkill = TrinketConfig.Bind<float>("Bracelets of the Brave", "PassiveClubSkill", 20f, "Club skill bonus while passively active."); BotB_PassiveBluntDamage = TrinketConfig.Bind<float>("Bracelets of the Brave", "PassiveBluntDamage", 0.15f, "Blunt damage modifier while passively active (0.15 = +15%)."); BotB_DoubledInstantHealth = TrinketConfig.Bind<float>("Bracelets of the Brave", "DoubledInstantHealth", 100f, "Health gain when adrenaline doubling activates."); BotB_DoubledClubSkill = TrinketConfig.Bind<float>("Bracelets of the Brave", "DoubledClubSkill", 40f, "Club skill bonus during adrenaline doubling."); BotB_DoubledBluntDamage = TrinketConfig.Bind<float>("Bracelets of the Brave", "DoubledBluntDamage", 0.3f, "Blunt damage modifier during adrenaline doubling."); BotB_Duration = TrinketConfig.Bind<float>("Bracelets of the Brave", "Duration", 60f, "Duration in seconds for the doubled effect."); EM_PassiveBlockStaminaReduction = TrinketConfig.Bind<float>("Evasion Mantle", "PassiveBlockStaminaReduction", 0.5f, "Block stamina cost reduction while passively active (0.50 = -50%)."); EM_PassiveParryBonus = TrinketConfig.Bind<float>("Evasion Mantle", "PassiveParryBonus", 0.5f, "Parry bonus multiplier while passively active (0.50 = +50%)."); EM_PassiveDodgeSkill = TrinketConfig.Bind<float>("Evasion Mantle", "PassiveDodgeSkill", 20f, "Dodge skill bonus while passively active."); EM_DoubledBlockStaminaReduction = TrinketConfig.Bind<float>("Evasion Mantle", "DoubledBlockStaminaReduction", 1f, "Block stamina cost reduction during adrenaline doubling (1.00 = -100%)."); EM_DoubledParryBonus = TrinketConfig.Bind<float>("Evasion Mantle", "DoubledParryBonus", 1f, "Parry bonus multiplier during adrenaline doubling."); EM_DoubledDodgeSkill = TrinketConfig.Bind<float>("Evasion Mantle", "DoubledDodgeSkill", 40f, "Dodge skill bonus during adrenaline doubling."); EM_Duration = TrinketConfig.Bind<float>("Evasion Mantle", "Duration", 120f, "Duration in seconds for the doubled effect."); PE_PassiveEitrRegen = TrinketConfig.Bind<float>("Pulsating Earrings", "PassiveEitrRegen", 1.25f, "Eitr regen multiplier while passively active (1.0 = no bonus, 1.25 = +25%)."); PE_DoubledEitrRegen = TrinketConfig.Bind<float>("Pulsating Earrings", "DoubledEitrRegen", 1.5f, "Eitr regen multiplier during adrenaline doubling."); PE_Duration = TrinketConfig.Bind<float>("Pulsating Earrings", "Duration", 60f, "Duration in seconds for the doubled effect."); RS_PassiveInstantStamina = TrinketConfig.Bind<float>("Resounding Shackle", "PassiveInstantStamina", 50f, "One-time stamina gain when trinket is equipped."); RS_PassiveSlashDamage = TrinketConfig.Bind<float>("Resounding Shackle", "PassiveSlashDamage", 0.15f, "Slash damage modifier while passively active (0.15 = +15%)."); RS_DoubledInstantStamina = TrinketConfig.Bind<float>("Resounding Shackle", "DoubledInstantStamina", 100f, "Stamina gain when adrenaline doubling activates."); RS_DoubledSlashDamage = TrinketConfig.Bind<float>("Resounding Shackle", "DoubledSlashDamage", 0.3f, "Slash damage modifier during adrenaline doubling."); RS_Duration = TrinketConfig.Bind<float>("Resounding Shackle", "Duration", 60f, "Duration in seconds for the doubled effect."); J_PassiveInstantEitr = TrinketConfig.Bind<float>("Jormundling", "PassiveInstantEitr", 50f, "One-time eitr gain when trinket is equipped."); J_PassiveElementalMagicSkill = TrinketConfig.Bind<float>("Jormundling", "PassiveElementalMagicSkill", 20f, "Elemental Magic skill bonus while passively active."); J_PassiveBloodMagicSkill = TrinketConfig.Bind<float>("Jormundling", "PassiveBloodMagicSkill", 20f, "Blood Magic skill bonus while passively active."); J_DoubledInstantEitr = TrinketConfig.Bind<float>("Jormundling", "DoubledInstantEitr", 100f, "Eitr gain when adrenaline doubling activates."); J_DoubledElementalMagicSkill = TrinketConfig.Bind<float>("Jormundling", "DoubledElementalMagicSkill", 40f, "Elemental Magic skill bonus during adrenaline doubling."); J_DoubledBloodMagicSkill = TrinketConfig.Bind<float>("Jormundling", "DoubledBloodMagicSkill", 40f, "Blood Magic skill bonus during adrenaline doubling."); J_Duration = TrinketConfig.Bind<float>("Jormundling", "Duration", 60f, "Duration in seconds for the doubled effect."); B_PassiveInstantHealth = TrinketConfig.Bind<float>("Brimstone", "PassiveInstantHealth", 50f, "One-time health gain when trinket is equipped."); B_PassiveInstantStamina = TrinketConfig.Bind<float>("Brimstone", "PassiveInstantStamina", 50f, "One-time stamina gain when trinket is equipped."); B_DoubledInstantHealth = TrinketConfig.Bind<float>("Brimstone", "DoubledInstantHealth", 100f, "Health gain when adrenaline doubling activates."); B_DoubledInstantStamina = TrinketConfig.Bind<float>("Brimstone", "DoubledInstantStamina", 100f, "Stamina gain when adrenaline doubling activates."); B_Duration = TrinketConfig.Bind<float>("Brimstone", "Duration", 60f, "Duration in seconds for the doubled effect."); } } [BepInPlugin("com.xavie.bettertrinkets", "BetterTrinkets", "1.0.0")] public class Plugin : BaseUnityPlugin { public const string PluginGuid = "com.xavie.bettertrinkets"; public const string PluginName = "BetterTrinkets"; public const string PluginVersion = "1.0.0"; internal static Plugin Instance; internal static ManualLogSource Log; private Harmony _harmony; private void Awake() { Instance = this; Log = ((BaseUnityPlugin)this).Logger; ModConfig.Init(); TrinketDefinitions.Init(); _harmony = Harmony.CreateAndPatchAll(Assembly.GetExecutingAssembly(), "com.xavie.bettertrinkets"); Log.LogInfo((object)"BetterTrinkets v1.0.0 loaded."); } private void OnDestroy() { Harmony harmony = _harmony; if (harmony != null) { harmony.UnpatchSelf(); } } } public class TrinketData { public string DisplayName; public string SEName; public float Duration; public Dictionary<string, object> PassiveFields; public Dictionary<string, object> DoubledFields; public float InstantHealth; public float InstantStamina; public float InstantEitr; public float DoubledInstantHealth; public float DoubledInstantStamina; public float DoubledInstantEitr; } public static class TrinketDefinitions { public static Dictionary<string, TrinketData> Effects; public static void Init() { Effects = new Dictionary<string, TrinketData> { ["TrinketBronzeHealth"] = new TrinketData { DisplayName = "Heart of the Forest", SEName = "TrinketBronzeHealth", Duration = ModConfig.HotF_Duration.Value, PassiveFields = new Dictionary<string, object> { ["m_healthRegenMultiplier"] = ModConfig.HotF_PassiveHealthRegen.Value }, DoubledFields = new Dictionary<string, object> { ["m_healthRegenMultiplier"] = ModConfig.HotF_DoubledHealthRegen.Value } }, ["TrinketBronzeStamina"] = new TrinketData { DisplayName = "Bronze Pendant", SEName = "TrinketBronzeStamina", Duration = ModConfig.BP_Duration.Value, PassiveFields = new Dictionary<string, object> { ["m_staminaRegenMultiplier"] = ModConfig.BP_PassiveStaminaRegen.Value }, DoubledFields = new Dictionary<string, object> { ["m_staminaRegenMultiplier"] = ModConfig.BP_DoubledStaminaRegen.Value } }, ["TrinketIronHealth"] = new TrinketData { DisplayName = "Iron Brooch", SEName = "TrinketIronHealth", Duration = ModConfig.IB_Duration.Value, InstantHealth = ModConfig.IB_PassiveInstantHealth.Value, DoubledInstantHealth = ModConfig.IB_DoubledInstantHealth.Value, PassiveFields = new Dictionary<string, object> { ["m_addArmor"] = ModConfig.IB_PassiveArmor.Value }, DoubledFields = new Dictionary<string, object> { ["m_addArmor"] = ModConfig.IB_DoubledArmor.Value } }, ["TrinketIronStamina"] = new TrinketData { DisplayName = "Nimble Anklet", SEName = "TrinketIronStamina", Duration = ModConfig.NA_Duration.Value, InstantStamina = ModConfig.NA_PassiveInstantStamina.Value, DoubledInstantStamina = ModConfig.NA_DoubledInstantStamina.Value, PassiveFields = new Dictionary<string, object> { ["m_speedModifier"] = ModConfig.NA_PassiveSpeed.Value }, DoubledFields = new Dictionary<string, object> { ["m_speedModifier"] = ModConfig.NA_DoubledSpeed.Value } }, ["TrinketChitinSwim"] = new TrinketData { DisplayName = "Fins of Destiny", SEName = "TrinketChitinSwim", Duration = ModConfig.FoD_Duration.Value, PassiveFields = new Dictionary<string, object> { ["m_swimSpeedModifier"] = ModConfig.FoD_PassiveSwimSpeed.Value }, DoubledFields = new Dictionary<string, object> { ["m_swimSpeedModifier"] = ModConfig.FoD_DoubledSwimSpeed.Value } }, ["TrinketSilverDamage"] = new TrinketData { DisplayName = "Wolf Sight", SEName = "TrinketSilverDamage", Duration = ModConfig.WS_Duration.Value, PassiveFields = new Dictionary<string, object> { ["m_skillLevel"] = (object)(SkillType)8, ["m_skillLevelModifier"] = ModConfig.WS_PassiveBowSkill.Value, ["m_skillLevel2"] = (object)(SkillType)101, ["m_skillLevelModifier2"] = ModConfig.WS_PassiveSneakSkill.Value, ["m_damageModifier"] = ModConfig.WS_PassivePierceDamage.Value }, DoubledFields = new Dictionary<string, object> { ["m_skillLevel"] = (object)(SkillType)8, ["m_skillLevelModifier"] = ModConfig.WS_DoubledBowSkill.Value, ["m_skillLevel2"] = (object)(SkillType)101, ["m_skillLevelModifier2"] = ModConfig.WS_DoubledSneakSkill.Value, ["m_damageModifier"] = ModConfig.WS_DoubledPierceDamage.Value } }, ["TrinketSilverResist"] = new TrinketData { DisplayName = "Crystal Heart", SEName = "TrinketSilverResist", Duration = ModConfig.CH_Duration.Value, PassiveFields = new Dictionary<string, object>(), DoubledFields = new Dictionary<string, object>() }, ["TrinketBlackDamageHealth"] = new TrinketData { DisplayName = "Bracelets of the Brave", SEName = "TrinketBlackDamageHealth", Duration = ModConfig.BotB_Duration.Value, InstantHealth = ModConfig.BotB_PassiveInstantHealth.Value, DoubledInstantHealth = ModConfig.BotB_DoubledInstantHealth.Value, PassiveFields = new Dictionary<string, object> { ["m_skillLevel"] = (object)(SkillType)3, ["m_skillLevelModifier"] = ModConfig.BotB_PassiveClubSkill.Value, ["m_damageModifier"] = ModConfig.BotB_PassiveBluntDamage.Value }, DoubledFields = new Dictionary<string, object> { ["m_skillLevel"] = (object)(SkillType)3, ["m_skillLevelModifier"] = ModConfig.BotB_DoubledClubSkill.Value, ["m_damageModifier"] = ModConfig.BotB_DoubledBluntDamage.Value } }, ["TrinketBlackStamina"] = new TrinketData { DisplayName = "Evasion Mantle", SEName = "TrinketBlackStamina", Duration = ModConfig.EM_Duration.Value, PassiveFields = new Dictionary<string, object> { ["m_skillLevel"] = (object)(SkillType)108, ["m_skillLevelModifier"] = ModConfig.EM_PassiveDodgeSkill.Value }, DoubledFields = new Dictionary<string, object> { ["m_skillLevel"] = (object)(SkillType)108, ["m_skillLevelModifier"] = ModConfig.EM_DoubledDodgeSkill.Value } }, ["TrinketCarapaceEitr"] = new TrinketData { DisplayName = "Pulsating Earrings", SEName = "TrinketCarapaceEitr", Duration = ModConfig.PE_Duration.Value, PassiveFields = new Dictionary<string, object> { ["m_eitrRegenMultiplier"] = ModConfig.PE_PassiveEitrRegen.Value }, DoubledFields = new Dictionary<string, object> { ["m_eitrRegenMultiplier"] = ModConfig.PE_DoubledEitrRegen.Value } }, ["TrinketScaleStaminaDamage"] = new TrinketData { DisplayName = "Resounding Shackle", SEName = "TrinketScaleStaminaDamage", Duration = ModConfig.RS_Duration.Value, InstantStamina = ModConfig.RS_PassiveInstantStamina.Value, DoubledInstantStamina = ModConfig.RS_DoubledInstantStamina.Value, PassiveFields = new Dictionary<string, object> { ["m_damageModifier"] = ModConfig.RS_PassiveSlashDamage.Value }, DoubledFields = new Dictionary<string, object> { ["m_damageModifier"] = ModConfig.RS_DoubledSlashDamage.Value } }, ["TrinketFlametalEitr"] = new TrinketData { DisplayName = "Jormundling", SEName = "TrinketFlametalEitr", Duration = ModConfig.J_Duration.Value, InstantEitr = ModConfig.J_PassiveInstantEitr.Value, DoubledInstantEitr = ModConfig.J_DoubledInstantEitr.Value, PassiveFields = new Dictionary<string, object> { ["m_skillLevel"] = (object)(SkillType)9, ["m_skillLevelModifier"] = ModConfig.J_PassiveElementalMagicSkill.Value, ["m_skillLevel2"] = (object)(SkillType)10, ["m_skillLevelModifier2"] = ModConfig.J_PassiveBloodMagicSkill.Value }, DoubledFields = new Dictionary<string, object> { ["m_skillLevel"] = (object)(SkillType)9, ["m_skillLevelModifier"] = ModConfig.J_DoubledElementalMagicSkill.Value, ["m_skillLevel2"] = (object)(SkillType)10, ["m_skillLevelModifier2"] = ModConfig.J_DoubledBloodMagicSkill.Value } }, ["TrinketFlametalStaminaHealth"] = new TrinketData { DisplayName = "Brimstone", SEName = "TrinketFlametalStaminaHealth", Duration = ModConfig.B_Duration.Value, InstantHealth = ModConfig.B_PassiveInstantHealth.Value, InstantStamina = ModConfig.B_PassiveInstantStamina.Value, DoubledInstantHealth = ModConfig.B_DoubledInstantHealth.Value, DoubledInstantStamina = ModConfig.B_DoubledInstantStamina.Value, PassiveFields = new Dictionary<string, object>(), DoubledFields = new Dictionary<string, object>() } }; } } internal static class HashHelper { public static int GetStableHashCode(string str) { int num = 5381; int num2 = num; for (int i = 0; i < str.Length && str[i] != 0; i += 2) { num = ((num << 5) + num) ^ str[i]; if (i == str.Length - 1 || str[i + 1] == '\0') { break; } num2 = ((num2 << 5) + num2) ^ str[i + 1]; } return num + num2 * 1566083941; } } public class TrinketEffectManager { private static TrinketEffectManager _instance; private string _currentTrinketSEName; private StatusEffect _activePassiveSE; private bool _isDoubled; private float _doubledTimeRemaining; private bool _instantEffectsApplied; public static TrinketEffectManager Instance => _instance ?? (_instance = new TrinketEffectManager()); public void OnPlayerUpdate(Player player) { if ((Object)(object)player != (Object)(object)Player.m_localPlayer || !ModConfig.Enabled.Value) { return; } string equippedTrinketSEName = GetEquippedTrinketSEName(player); SEMan sEMan = ((Character)player).GetSEMan(); if (equippedTrinketSEName != _currentTrinketSEName) { RemovePassiveEffect(sEMan); _currentTrinketSEName = equippedTrinketSEName; _isDoubled = false; _doubledTimeRemaining = 0f; _instantEffectsApplied = false; if (_currentTrinketSEName != null) { ApplyPassiveEffect(player, sEMan, _currentTrinketSEName); } } if (_isDoubled && _doubledTimeRemaining > 0f) { _doubledTimeRemaining -= Time.deltaTime; if (_doubledTimeRemaining <= 0f) { _isDoubled = false; UpdateEffectValues(_activePassiveSE, _currentTrinketSEName, doubled: false); LogDebug("Doubled effect expired for " + GetDisplayName(_currentTrinketSEName) + ", reverted to passive."); } } if (_currentTrinketSEName != null && (Object)(object)_activePassiveSE == (Object)null) { ApplyPassiveEffect(player, sEMan, _currentTrinketSEName); } } public void OnAdrenalineActivated(Player player, float duration) { if (_currentTrinketSEName != null && ModConfig.DoublingOnAdrenaline.Value && TrinketDefinitions.Effects.TryGetValue(_currentTrinketSEName, out var value)) { float num = ((value.Duration > 0f) ? value.Duration : duration); _isDoubled = true; _doubledTimeRemaining = num; UpdateEffectValues(_activePassiveSE, _currentTrinketSEName, doubled: true); ApplyInstantEffects(player, value, doubled: true); Plugin.Log.LogInfo((object)$"DOUBLED effect for {value.DisplayName} for {num}s!"); } } public void Reset() { _currentTrinketSEName = null; _activePassiveSE = null; _isDoubled = false; _doubledTimeRemaining = 0f; _instantEffectsApplied = false; LogDebug("State reset."); } private string GetEquippedTrinketSEName(Player player) { //IL_003a: Unknown result type (might be due to invalid IL or missing references) //IL_0041: Invalid comparison between Unknown and I4 Inventory inventory = ((Humanoid)player).GetInventory(); if (inventory == null) { return null; } foreach (ItemData allItem in inventory.GetAllItems()) { if ((int)allItem.m_shared.m_itemType == 24 && ((Humanoid)player).IsItemEquiped(allItem)) { StatusEffect equipStatusEffect = allItem.m_shared.m_equipStatusEffect; if ((Object)(object)equipStatusEffect != (Object)null) { LogDebug("Found equipped trinket: " + allItem.m_shared.m_name + ", SE: " + ((Object)equipStatusEffect).name); return ((Object)equipStatusEffect).name; } equipStatusEffect = allItem.m_shared.m_attackStatusEffect; if ((Object)(object)equipStatusEffect != (Object)null) { LogDebug("Found trinket SE via m_attackStatusEffect: " + ((Object)equipStatusEffect).name); return ((Object)equipStatusEffect).name; } equipStatusEffect = allItem.m_shared.m_setStatusEffect; if ((Object)(object)equipStatusEffect != (Object)null) { LogDebug("Found trinket SE via m_setStatusEffect: " + ((Object)equipStatusEffect).name); return ((Object)equipStatusEffect).name; } GameObject dropPrefab = allItem.m_dropPrefab; string text = ((dropPrefab != null) ? ((Object)dropPrefab).name : null); if (text != null && TrinketDefinitions.Effects.ContainsKey(text)) { LogDebug("Matched trinket by prefab name: " + text); return text; } Plugin.Log.LogWarning((object)("Equipped trinket '" + allItem.m_shared.m_name + "' has no recognizable SE. Prefab: " + text)); return null; } } return null; } private void ApplyPassiveEffect(Player player, SEMan seMan, string seName) { if (!TrinketDefinitions.Effects.TryGetValue(seName, out var value)) { Plugin.Log.LogWarning((object)("No trinket definition for SE '" + seName + "'. Effect will not be applied.")); return; } ObjectDB instance = ObjectDB.instance; if ((Object)(object)instance == (Object)null) { Plugin.Log.LogWarning((object)"ObjectDB not available yet."); return; } StatusEffect statusEffect = instance.GetStatusEffect(HashHelper.GetStableHashCode(seName)); if ((Object)(object)statusEffect == (Object)null) { Plugin.Log.LogWarning((object)("SE '" + seName + "' not found in ObjectDB. Is the name correct?")); return; } StatusEffect statusEffect2 = seMan.GetStatusEffect(HashHelper.GetStableHashCode(seName)); if ((Object)(object)statusEffect2 != (Object)null) { seMan.RemoveStatusEffect(statusEffect2, true); LogDebug("Removed existing vanilla SE '" + seName + "' before applying passive."); } StatusEffect val = Object.Instantiate<StatusEffect>(statusEffect); val.m_ttl = 0f; UpdateEffectValues(val, seName, _isDoubled); _activePassiveSE = seMan.AddStatusEffect(val, false, 0, 0f); if ((Object)(object)_activePassiveSE != (Object)null) { _activePassiveSE.m_ttl = 0f; Plugin.Log.LogInfo((object)("Applied passive " + value.DisplayName + " effect.")); } else { Plugin.Log.LogWarning((object)("Failed to add passive SE for " + value.DisplayName + ".")); } if (!_instantEffectsApplied) { ApplyInstantEffects(player, value, doubled: false); _instantEffectsApplied = true; } } private void RemovePassiveEffect(SEMan seMan) { if ((Object)(object)_activePassiveSE != (Object)null) { seMan.RemoveStatusEffect(_activePassiveSE, true); LogDebug("Removed passive SE '" + ((Object)_activePassiveSE).name + "'."); _activePassiveSE = null; } } private void UpdateEffectValues(StatusEffect se, string seName, bool doubled) { if ((Object)(object)se == (Object)null || !TrinketDefinitions.Effects.TryGetValue(seName, out var value)) { return; } Dictionary<string, object> dictionary = (doubled ? value.DoubledFields : value.PassiveFields); foreach (KeyValuePair<string, object> item in dictionary) { FieldInfo fieldInfo = AccessTools.Field(((object)se).GetType(), item.Key); if (fieldInfo != null) { try { if (fieldInfo.FieldType == item.Value.GetType()) { fieldInfo.SetValue(se, item.Value); } else { fieldInfo.SetValue(se, Convert.ChangeType(item.Value, fieldInfo.FieldType)); } LogDebug($" Set {item.Key} = {item.Value} on {((Object)se).name}"); } catch (Exception ex) { Plugin.Log.LogWarning((object)("Failed to set field '" + item.Key + "' on " + ((object)se).GetType().Name + ": " + ex.Message)); } } else { Plugin.Log.LogWarning((object)("Field '" + item.Key + "' not found on " + ((object)se).GetType().Name + ". This field may need decompiler verification.")); } } if (seName == "TrinketSilverResist") { ApplyCrystalHeartResistance(se, doubled); } } private void ApplyCrystalHeartResistance(StatusEffect se, bool doubled) { //IL_0088: 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_0098: 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_009e: Unknown result type (might be due to invalid IL or missing references) //IL_00a9: Unknown result type (might be due to invalid IL or missing references) //IL_00b2: 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) //IL_00ba: Unknown result type (might be due to invalid IL or missing references) //IL_00bf: Unknown result type (might be due to invalid IL or missing references) //IL_00ca: Unknown result type (might be due to invalid IL or missing references) //IL_00d3: Unknown result type (might be due to invalid IL or missing references) //IL_00da: Unknown result type (might be due to invalid IL or missing references) //IL_00db: Unknown result type (might be due to invalid IL or missing references) //IL_00e0: Unknown result type (might be due to invalid IL or missing references) //IL_00f8: Unknown result type (might be due to invalid IL or missing references) //IL_007e: Unknown result type (might be due to invalid IL or missing references) FieldInfo fieldInfo = AccessTools.Field(((object)se).GetType(), "m_mods"); if (fieldInfo == null) { Plugin.Log.LogWarning((object)"m_mods field not found on Crystal Heart SE. Resistance effects may not work."); return; } string text = (doubled ? ModConfig.CH_DoubledResistance.Value : ModConfig.CH_PassiveResistance.Value); if (!Enum.TryParse<DamageModifier>(text, ignoreCase: true, out DamageModifier result)) { Plugin.Log.LogWarning((object)("Invalid resistance level '" + text + "'. Use: Normal, Resistant, VeryResistant, Immune.")); result = (DamageModifier)1; } List<DamageModPair> value = new List<DamageModPair> { new DamageModPair { m_type = (DamageType)1, m_modifier = result }, new DamageModPair { m_type = (DamageType)2, m_modifier = result }, new DamageModPair { m_type = (DamageType)4, m_modifier = result } }; fieldInfo.SetValue(se, value); LogDebug($" Set Crystal Heart resistance to {result}"); } private void ApplyInstantEffects(Player player, TrinketData data, bool doubled) { float num = (doubled ? data.DoubledInstantHealth : data.InstantHealth); float num2 = (doubled ? data.DoubledInstantStamina : data.InstantStamina); float num3 = (doubled ? data.DoubledInstantEitr : data.InstantEitr); if (num > 0f) { ((Character)player).Heal(num, true); LogDebug($" Healed {num} HP"); } if (num2 > 0f) { ((Character)player).AddStamina(num2); LogDebug($" Added {num2} stamina"); } if (!(num3 > 0f)) { return; } try { MethodInfo methodInfo = AccessTools.Method(typeof(Player), "AddEitr", new Type[1] { typeof(float) }, (Type[])null); if (methodInfo != null) { methodInfo.Invoke(player, new object[1] { num3 }); LogDebug($" Added {num3} eitr"); } else { Plugin.Log.LogWarning((object)"Player.AddEitr method not found. Eitr gain not applied."); } } catch (Exception ex) { Plugin.Log.LogWarning((object)("Failed to add eitr: " + ex.Message)); } } private string GetDisplayName(string seName) { if (seName != null && TrinketDefinitions.Effects.TryGetValue(seName, out var value)) { return value.DisplayName; } return seName ?? "None"; } private void LogDebug(string message) { if (ModConfig.DebugLogging.Value) { Plugin.Log.LogInfo((object)("[Debug] " + message)); } } } } namespace BetterTrinkets.Patches { [HarmonyPatch(typeof(Player))] public static class PlayerPatches { [HarmonyPatch("Update")] [HarmonyPostfix] public static void Update_Postfix(Player __instance) { TrinketEffectManager.Instance.OnPlayerUpdate(__instance); } [HarmonyPatch("OnDeath")] [HarmonyPostfix] public static void OnDeath_Postfix(Player __instance) { if ((Object)(object)__instance == (Object)(object)Player.m_localPlayer) { TrinketEffectManager.Instance.Reset(); } } [HarmonyPatch("OnSpawned")] [HarmonyPostfix] public static void OnSpawned_Postfix(Player __instance) { if ((Object)(object)__instance == (Object)(object)Player.m_localPlayer) { TrinketEffectManager.Instance.OnPlayerUpdate(__instance); } } } [HarmonyPatch(typeof(SEMan))] public static class SEManPatches { [HarmonyPatch("AddStatusEffect", new Type[] { typeof(StatusEffect), typeof(bool), typeof(int), typeof(float) })] [HarmonyPrefix] public static bool AddStatusEffect_SE_Prefix(SEMan __instance, StatusEffect statusEffect, ref StatusEffect __result) { if (!ModConfig.Enabled.Value) { return true; } if ((Object)(object)statusEffect == (Object)null) { return true; } string name = ((Object)statusEffect).name; if (name == null || !name.StartsWith("Trinket")) { return true; } if (!TrinketDefinitions.Effects.ContainsKey(name)) { return true; } Character value = Traverse.Create((object)__instance).Field("m_character").GetValue<Character>(); if ((Object)(object)value == (Object)null) { return true; } Player val = (Player)(object)((value is Player) ? value : null); if ((Object)(object)val == (Object)null || (Object)(object)val != (Object)(object)Player.m_localPlayer) { return true; } float ttl = statusEffect.m_ttl; if (ModConfig.DebugLogging.Value) { Plugin.Log.LogInfo((object)$"[Debug] Intercepted vanilla trinket SE add: {name} (ttl={ttl}s)"); } TrinketEffectManager.Instance.OnAdrenalineActivated(val, ttl); __result = null; return false; } [HarmonyPatch("AddStatusEffect", new Type[] { typeof(int), typeof(bool), typeof(int), typeof(float) })] [HarmonyPrefix] public static bool AddStatusEffect_Hash_Prefix(SEMan __instance, int nameHash, ref StatusEffect __result) { if (!ModConfig.Enabled.Value) { return true; } foreach (KeyValuePair<string, TrinketData> effect in TrinketDefinitions.Effects) { if (HashHelper.GetStableHashCode(effect.Key) == nameHash) { Character value = Traverse.Create((object)__instance).Field("m_character").GetValue<Character>(); if ((Object)(object)value == (Object)null) { return true; } Player val = (Player)(object)((value is Player) ? value : null); if ((Object)(object)val == (Object)null || (Object)(object)val != (Object)(object)Player.m_localPlayer) { return true; } float duration = effect.Value.Duration; if (ModConfig.DebugLogging.Value) { Plugin.Log.LogInfo((object)$"[Debug] Intercepted hash-based trinket SE add: {effect.Key} (hash={nameHash})"); } TrinketEffectManager.Instance.OnAdrenalineActivated(val, duration); __result = null; return false; } } return true; } } } namespace System.Runtime.CompilerServices { [AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)] internal sealed class IgnoresAccessChecksToAttribute : Attribute { public IgnoresAccessChecksToAttribute(string assemblyName) { } } }