using 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)
{
}
}
}