Decompiled source of Passive Trinket Modifiers v0.35.1

plugins/TrinketModifiers.dll

Decompiled 2 weeks ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Text;
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.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")]
[assembly: AssemblyCompany("TrinketModifiers")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("0.32.2.0")]
[assembly: AssemblyInformationalVersion("1.0.0")]
[assembly: AssemblyProduct("TrinketModifiers")]
[assembly: AssemblyTitle("TrinketModifiers")]
[assembly: AssemblyVersion("0.32.2.0")]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)]
	internal sealed class NullableAttribute : Attribute
	{
		public readonly byte[] NullableFlags;

		public NullableAttribute(byte P_0)
		{
			NullableFlags = new byte[1] { P_0 };
		}

		public NullableAttribute(byte[] P_0)
		{
			NullableFlags = P_0;
		}
	}
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)]
	internal sealed class NullableContextAttribute : Attribute
	{
		public readonly byte Flag;

		public NullableContextAttribute(byte P_0)
		{
			Flag = P_0;
		}
	}
	[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 TrinketModifiers
{
	[BepInPlugin("com.gdall.trinketmodifiers", "Trinket Modifiers", "0.32.2")]
	public sealed class Plugin : BaseUnityPlugin
	{
		public const string PluginGuid = "com.gdall.trinketmodifiers";

		public const string PluginName = "Trinket Modifiers";

		public const string PluginVersion = "0.32.2";

		internal static ManualLogSource Log;

		private ConfigEntry<bool> _enabled;

		private ConfigEntry<bool> _debugLogging;

		internal static ConfigEntry<bool> DoublingOnAdrenaline;

		internal static ConfigEntry<float> AdrenalineGainMultiplier;

		internal static bool DebugLogging { get; private set; }

		private void Awake()
		{
			//IL_00c1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00cb: Expected O, but got Unknown
			//IL_01a5: Unknown result type (might be due to invalid IL or missing references)
			Log = ((BaseUnityPlugin)this).Logger;
			_enabled = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "Enabled", true, "Enables the Trinket Modifiers plugin.");
			_debugLogging = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "DebugLogging", false, "Writes additional diagnostic messages to LogOutput.log.");
			DebugLogging = _debugLogging.Value;
			DoublingOnAdrenaline = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "DoublingOnAdrenaline", false, "When adrenaline fills, use each equipped trinket's doubled values for its configured duration.");
			DoublingOnAdrenaline.SettingChanged += OnDoublingOnAdrenalineChanged;
			AdrenalineGainMultiplier = ((BaseUnityPlugin)this).Config.Bind<float>("General", "AdrenalineGainMultiplier", 0f, new ConfigDescription("Global adrenaline gain multiplier. 0.0 disables gain; 1.0 is unchanged; 2.0 is double gain.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, float.MaxValue), Array.Empty<object>()));
			AdrenalineGainMultiplier.SettingChanged += OnAdrenalineGainMultiplierChanged;
			ClampAdrenalineGainMultiplier();
			TrinketConfigRegistry.Initialize(((BaseUnityPlugin)this).Config);
			((BaseUnityPlugin)this).Logger.LogInfo((object)"Trinket Modifiers v0.32.2 loaded.");
			((BaseUnityPlugin)this).Logger.LogInfo((object)("Configuration created: " + ((BaseUnityPlugin)this).Config.ConfigFilePath));
			((BaseUnityPlugin)this).Logger.LogInfo((object)($"Enabled={_enabled.Value}; DebugLogging={_debugLogging.Value}; " + $"DoublingOnAdrenaline={DoublingOnAdrenaline.Value}; " + $"AdrenalineGainMultiplier={AdrenalineGainMultiplier.Value}"));
			if (_enabled.Value)
			{
				new Harmony("com.gdall.trinketmodifiers").PatchAll();
				((BaseUnityPlugin)this).Logger.LogInfo((object)"Native configurable passive trinket effect engine installed.");
				((BaseUnityPlugin)this).Logger.LogInfo((object)("Save safety protection active: player serialization target found=" + $"{PlayerSaveDataPatch.IsAvailable}. Runtime cloned status effects are removed before saving."));
				((BaseUnityPlugin)this).Logger.LogInfo((object)"Native SE_Stats path active: persistent passive modifiers are written to one status effect per equipped trinket.");
				((BaseUnityPlugin)this).Logger.LogInfo((object)$"Configured item-hover tooltip patch active: target found={TrinketTooltipPatch.IsAvailable}.");
			}
			if (_debugLogging.Value)
			{
				((BaseUnityPlugin)this).Logger.LogInfo((object)"Debug logging is enabled.");
			}
		}

		private void OnAdrenalineGainMultiplierChanged(object sender, EventArgs args)
		{
			ClampAdrenalineGainMultiplier();
		}

		private void OnDoublingOnAdrenalineChanged(object sender, EventArgs args)
		{
			TrinketEffectManager.OnDoublingSettingChanged(DoublingOnAdrenaline.Value);
		}

		private void ClampAdrenalineGainMultiplier()
		{
			float value = AdrenalineGainMultiplier.Value;
			if (float.IsNaN(value) || float.IsInfinity(value) || value < 0f)
			{
				AdrenalineGainMultiplier.Value = 0f;
				((BaseUnityPlugin)this).Logger.LogWarning((object)($"Clamped General/AdrenalineGainMultiplier from '{value}' to '0.0'. " + "Negative values are not supported."));
			}
		}
	}
	[HarmonyPatch(typeof(Player), "OnSpawned")]
	internal static class PlayerSpawnPatch
	{
		[HarmonyPostfix]
		private static void Postfix(Player __instance)
		{
			TrinketEffectManager.OnPlayerSpawned(__instance);
		}
	}
	internal sealed class TrinketDefinition
	{
		private readonly Dictionary<string, ConfigEntry<float>> _passiveDamage;

		private readonly Dictionary<string, ConfigEntry<float>> _doubledDamage;

		internal static readonly string[] DamageTypes = new string[10] { "Blunt", "Slash", "Pierce", "Chop", "Pickaxe", "Fire", "Frost", "Lightning", "Poison", "Spirit" };

		internal static readonly string[] SkillNames = new string[23]
		{
			"None", "Axes", "Bows", "Clubs", "Crossbows", "ElementalMagic", "BloodMagic", "Dodge", "Sneak", "Knives",
			"Pickaxes", "Polearms", "Run", "Spears", "Swords", "Unarmed", "Blocking", "Jump", "Swim", "Fishing",
			"Riding", "WoodCutting", "Farming"
		};

		internal string Section { get; }

		internal string DoubledSection { get; }

		internal string DisplayName { get; }

		internal string PrefabName { get; }

		internal IReadOnlyList<string> PrefabAliases { get; }

		internal ConfigEntry<float> PassiveHealthRegen { get; }

		internal ConfigEntry<float> PassiveStaminaRegen { get; }

		internal ConfigEntry<float> PassiveEitrRegen { get; }

		internal ConfigEntry<float> DoubledHealthRegen { get; }

		internal ConfigEntry<float> DoubledStaminaRegen { get; }

		internal ConfigEntry<float> DoubledEitrRegen { get; }

		internal ConfigEntry<float> PassiveArmor { get; }

		internal ConfigEntry<float> DoubledArmor { get; }

		internal ConfigEntry<float> PassiveSpeed { get; }

		internal ConfigEntry<float> DoubledSpeed { get; }

		internal ConfigEntry<float> PassiveSwimSpeed { get; }

		internal ConfigEntry<float> DoubledSwimSpeed { get; }

		internal ConfigEntry<float> PassiveJump { get; }

		internal ConfigEntry<float> DoubledJump { get; }

		internal ConfigEntry<float> PassiveFallDamage { get; }

		internal ConfigEntry<float> DoubledFallDamage { get; }

		internal ConfigEntry<float> PassiveNoise { get; }

		internal ConfigEntry<float> DoubledNoise { get; }

		internal ConfigEntry<float> PassiveStealth { get; }

		internal ConfigEntry<float> DoubledStealth { get; }

		internal ConfigEntry<float> PassiveMaxCarryWeight { get; }

		internal ConfigEntry<float> DoubledMaxCarryWeight { get; }

		internal ConfigEntry<float> PassiveTimedBlockBonus { get; }

		internal ConfigEntry<float> DoubledTimedBlockBonus { get; }

		internal ConfigEntry<float> PassiveStagger { get; }

		internal ConfigEntry<float> DoubledStagger { get; }

		internal ConfigEntry<float> PassiveDamageModifier { get; }

		internal ConfigEntry<float> DoubledDamageModifier { get; }

		internal ConfigEntry<float> PassiveRunStaminaUse { get; }

		internal ConfigEntry<float> DoubledRunStaminaUse { get; }

		internal ConfigEntry<float> PassiveJumpStaminaUse { get; }

		internal ConfigEntry<float> DoubledJumpStaminaUse { get; }

		internal ConfigEntry<float> PassiveAttackStaminaUse { get; }

		internal ConfigEntry<float> DoubledAttackStaminaUse { get; }

		internal ConfigEntry<float> PassiveBlockStaminaUse { get; }

		internal ConfigEntry<float> DoubledBlockStaminaUse { get; }

		internal ConfigEntry<float> PassiveDodgeStaminaUse { get; }

		internal ConfigEntry<float> DoubledDodgeStaminaUse { get; }

		internal ConfigEntry<float> PassiveSwimStaminaUse { get; }

		internal ConfigEntry<float> DoubledSwimStaminaUse { get; }

		internal ConfigEntry<float> PassiveHomeItemStaminaUse { get; }

		internal ConfigEntry<float> DoubledHomeItemStaminaUse { get; }

		internal ConfigEntry<float> PassiveSneakStaminaUse { get; }

		internal ConfigEntry<float> DoubledSneakStaminaUse { get; }

		internal ConfigEntry<string> PassiveResistance { get; }

		internal ConfigEntry<string> DoubledResistance { get; }

		internal ConfigEntry<string> PassiveSkill1 { get; }

		internal ConfigEntry<float> PassiveSkill1Bonus { get; }

		internal ConfigEntry<string> PassiveSkill2 { get; }

		internal ConfigEntry<float> PassiveSkill2Bonus { get; }

		internal ConfigEntry<string> PassiveRaiseSkill { get; }

		internal ConfigEntry<float> PassiveRaiseSkillBonus { get; }

		internal ConfigEntry<string> DoubledSkill1 { get; }

		internal ConfigEntry<float> DoubledSkill1Bonus { get; }

		internal ConfigEntry<string> DoubledSkill2 { get; }

		internal ConfigEntry<float> DoubledSkill2Bonus { get; }

		internal ConfigEntry<string> DoubledRaiseSkill { get; }

		internal ConfigEntry<float> DoubledRaiseSkillBonus { get; }

		internal ConfigEntry<float> Duration { get; }

		internal TrinketDefinition(ConfigFile config, string section, string displayName, string prefabName, Dictionary<string, float> numericDefaults, Dictionary<string, string> textDefaults, string passiveResistanceDefault, string doubledResistanceDefault, string[]? prefabAliases)
		{
			Section = section;
			DisplayName = displayName;
			PrefabName = prefabName;
			PrefabAliases = prefabAliases ?? Array.Empty<string>();
			DoubledSection = section + ".Doubled";
			PassiveHealthRegen = BindFloat(config, Section, "HealthRegenMultiplier", numericDefaults, 1f, "Health regeneration multiplier. 1.15 means +15%.");
			PassiveStaminaRegen = BindFloat(config, Section, "StaminaRegenMultiplier", numericDefaults, 1f, "Stamina regeneration multiplier. 1.15 means +15%.");
			PassiveEitrRegen = BindFloat(config, Section, "EitrRegenMultiplier", numericDefaults, 1f, "Eitr regeneration multiplier. 1.15 means +15%.");
			DoubledHealthRegen = BindFloat(config, DoubledSection, "HealthRegenMultiplier", numericDefaults, 1f, "Health regeneration multiplier during adrenaline doubling.");
			DoubledStaminaRegen = BindFloat(config, DoubledSection, "StaminaRegenMultiplier", numericDefaults, 1f, "Stamina regeneration multiplier during adrenaline doubling.");
			DoubledEitrRegen = BindFloat(config, DoubledSection, "EitrRegenMultiplier", numericDefaults, 1f, "Eitr regeneration multiplier during adrenaline doubling.");
			PassiveArmor = BindFloat(config, Section, "Armor", numericDefaults, 0f, "Flat armor while active.");
			DoubledArmor = BindFloat(config, DoubledSection, "Armor", numericDefaults, 0f, "Flat armor during doubling.");
			PassiveSpeed = BindFloat(config, Section, "Speed", numericDefaults, 0f, "Movement-speed modifier. 0.10 means +10%.");
			DoubledSpeed = BindFloat(config, DoubledSection, "Speed", numericDefaults, 0f, "Movement-speed modifier during doubling.");
			PassiveSwimSpeed = BindFloat(config, Section, "SwimSpeed", numericDefaults, 0f, "Swim-speed modifier. 0.50 means +50%.");
			DoubledSwimSpeed = BindFloat(config, DoubledSection, "SwimSpeed", numericDefaults, 0f, "Swim-speed modifier during doubling.");
			PassiveJump = BindFloat(config, Section, "Jump", numericDefaults, 0f, "Jump modifier.");
			DoubledJump = BindFloat(config, DoubledSection, "Jump", numericDefaults, 0f, "Jump modifier during doubling.");
			PassiveFallDamage = BindFloat(config, Section, "FallDamageMultiplier", numericDefaults, 1f, "Fall-damage multiplier. 0.75 means 25% less fall damage.");
			DoubledFallDamage = BindFloat(config, DoubledSection, "FallDamageMultiplier", numericDefaults, 1f, "Fall-damage multiplier during doubling.");
			PassiveNoise = BindFloat(config, Section, "NoiseMultiplier", numericDefaults, 1f, "Noise multiplier. 1.0 is unchanged; 0.75 means 25% less noise.");
			DoubledNoise = BindFloat(config, DoubledSection, "NoiseMultiplier", numericDefaults, 1f, "Noise multiplier during doubling.");
			PassiveStealth = BindFloat(config, Section, "StealthMultiplier", numericDefaults, 1f, "Stealth multiplier. 1.0 is unchanged; 1.25 means +25% stealth.");
			DoubledStealth = BindFloat(config, DoubledSection, "StealthMultiplier", numericDefaults, 1f, "Stealth multiplier during doubling.");
			PassiveMaxCarryWeight = BindFloat(config, Section, "MaxCarryWeight", numericDefaults, 0f, "Flat maximum carry-weight bonus.");
			DoubledMaxCarryWeight = BindFloat(config, DoubledSection, "MaxCarryWeight", numericDefaults, 0f, "Flat maximum carry-weight bonus during doubling.");
			PassiveTimedBlockBonus = BindFloat(config, Section, "TimedBlockBonus", numericDefaults, 0f, "Timed-block bonus.");
			DoubledTimedBlockBonus = BindFloat(config, DoubledSection, "TimedBlockBonus", numericDefaults, 0f, "Timed-block bonus during doubling.");
			PassiveStagger = BindFloat(config, Section, "StaggerMultiplier", numericDefaults, 1f, "Stagger multiplier. 1.0 is unchanged; 0.75 means 25% less stagger.");
			DoubledStagger = BindFloat(config, DoubledSection, "StaggerMultiplier", numericDefaults, 1f, "Stagger multiplier during doubling.");
			PassiveDamageModifier = BindFloat(config, Section, "DamageModifier", numericDefaults, 0f, "General damage modifier.");
			DoubledDamageModifier = BindFloat(config, DoubledSection, "DamageModifier", numericDefaults, 0f, "General damage modifier during doubling.");
			PassiveRunStaminaUse = BindFloat(config, Section, "RunStaminaUseMultiplier", numericDefaults, 1f, "Run-stamina-use multiplier. 0.75 means 25% less cost.");
			DoubledRunStaminaUse = BindFloat(config, DoubledSection, "RunStaminaUseMultiplier", numericDefaults, 1f, "Run-stamina-use multiplier during doubling.");
			PassiveJumpStaminaUse = BindFloat(config, Section, "JumpStaminaUseMultiplier", numericDefaults, 1f, "Jump-stamina-use multiplier. 0.75 means 25% less cost.");
			DoubledJumpStaminaUse = BindFloat(config, DoubledSection, "JumpStaminaUseMultiplier", numericDefaults, 1f, "Jump-stamina-use multiplier during doubling.");
			PassiveAttackStaminaUse = BindFloat(config, Section, "AttackStaminaUseMultiplier", numericDefaults, 1f, "Attack-stamina-use multiplier. 0.75 means 25% less cost.");
			DoubledAttackStaminaUse = BindFloat(config, DoubledSection, "AttackStaminaUseMultiplier", numericDefaults, 1f, "Attack-stamina-use multiplier during doubling.");
			PassiveBlockStaminaUse = BindFloat(config, Section, "BlockStaminaUseMultiplier", numericDefaults, 1f, "Block-stamina-use multiplier. 0.75 means 25% less cost.");
			DoubledBlockStaminaUse = BindFloat(config, DoubledSection, "BlockStaminaUseMultiplier", numericDefaults, 1f, "Block-stamina-use multiplier during doubling.");
			PassiveDodgeStaminaUse = BindFloat(config, Section, "DodgeStaminaUseMultiplier", numericDefaults, 1f, "Dodge-stamina-use multiplier. 0.75 means 25% less cost.");
			DoubledDodgeStaminaUse = BindFloat(config, DoubledSection, "DodgeStaminaUseMultiplier", numericDefaults, 1f, "Dodge-stamina-use multiplier during doubling.");
			PassiveSwimStaminaUse = BindFloat(config, Section, "SwimStaminaUseMultiplier", numericDefaults, 1f, "Swim-stamina-use multiplier. 0.75 means 25% less cost.");
			DoubledSwimStaminaUse = BindFloat(config, DoubledSection, "SwimStaminaUseMultiplier", numericDefaults, 1f, "Swim-stamina-use multiplier during doubling.");
			PassiveHomeItemStaminaUse = BindFloat(config, Section, "HomeItemStaminaUseMultiplier", numericDefaults, 1f, "Home-item-stamina-use multiplier. 0.75 means 25% less cost.");
			DoubledHomeItemStaminaUse = BindFloat(config, DoubledSection, "HomeItemStaminaUseMultiplier", numericDefaults, 1f, "Home-item-stamina-use multiplier during doubling.");
			PassiveSneakStaminaUse = BindFloat(config, Section, "SneakStaminaUseMultiplier", numericDefaults, 1f, "Sneak-stamina-use multiplier. 0.75 means 25% less cost.");
			DoubledSneakStaminaUse = BindFloat(config, DoubledSection, "SneakStaminaUseMultiplier", numericDefaults, 1f, "Sneak-stamina-use multiplier during doubling.");
			PassiveResistance = config.Bind<string>(Section, "Resistance", passiveResistanceDefault, "Damage resistance: Normal, SlightlyResistant, Resistant, VeryResistant, or Immune.");
			DoubledResistance = config.Bind<string>(DoubledSection, "Resistance", doubledResistanceDefault, "Damage resistance during doubling: Normal, SlightlyResistant, Resistant, VeryResistant, or Immune.");
			PassiveSkill1 = BindSkill(config, Section, "Skill1", textDefaults, "None");
			PassiveSkill1Bonus = BindFloat(config, Section, "Skill1Bonus", numericDefaults, 0f, "Flat bonus to the selected first skill.");
			PassiveSkill2 = BindSkill(config, Section, "Skill2", textDefaults, "None");
			PassiveSkill2Bonus = BindFloat(config, Section, "Skill2Bonus", numericDefaults, 0f, "Flat bonus to the selected second skill.");
			PassiveRaiseSkill = BindSkill(config, Section, "RaiseSkill", textDefaults, "None");
			PassiveRaiseSkillBonus = BindFloat(config, Section, "RaiseSkillBonus", numericDefaults, 1f, "Skill-gain multiplier for the selected skill. 1.25 means +25% gain.");
			DoubledSkill1 = BindSkill(config, DoubledSection, "Skill1", textDefaults, "None");
			DoubledSkill1Bonus = BindFloat(config, DoubledSection, "Skill1Bonus", numericDefaults, 0f, "Flat bonus to the selected first skill during doubling.");
			DoubledSkill2 = BindSkill(config, DoubledSection, "Skill2", textDefaults, "None");
			DoubledSkill2Bonus = BindFloat(config, DoubledSection, "Skill2Bonus", numericDefaults, 0f, "Flat bonus to the selected second skill during doubling.");
			DoubledRaiseSkill = BindSkill(config, DoubledSection, "RaiseSkill", textDefaults, "None");
			DoubledRaiseSkillBonus = BindFloat(config, DoubledSection, "RaiseSkillBonus", numericDefaults, 1f, "Skill-gain multiplier during doubling. 1.25 means +25% gain.");
			Duration = BindFloat(config, DoubledSection, "Duration", numericDefaults, 0f, "Duration in seconds for adrenaline doubling.");
			_passiveDamage = new Dictionary<string, ConfigEntry<float>>(StringComparer.OrdinalIgnoreCase);
			_doubledDamage = new Dictionary<string, ConfigEntry<float>>(StringComparer.OrdinalIgnoreCase);
			string[] damageTypes = DamageTypes;
			foreach (string text in damageTypes)
			{
				_passiveDamage[text] = BindFloat(config, Section, text + "Damage", numericDefaults, 0f, text + " damage modifier. 0.15 means +15%.");
				_doubledDamage[text] = BindFloat(config, DoubledSection, text + "Damage", numericDefaults, 0f, text + " damage modifier during doubling.");
			}
		}

		internal string GetSkillName(int slot, bool doubled)
		{
			return NormalizeSkill(((slot != 1) ? (doubled ? DoubledSkill2 : PassiveSkill2) : (doubled ? DoubledSkill1 : PassiveSkill1)).Value);
		}

		internal float GetSkillBonus(int slot, bool doubled)
		{
			return ((slot != 1) ? (doubled ? DoubledSkill2Bonus : PassiveSkill2Bonus) : (doubled ? DoubledSkill1Bonus : PassiveSkill1Bonus)).Value;
		}

		internal string GetRaiseSkillName(bool doubled)
		{
			return NormalizeSkill((doubled ? DoubledRaiseSkill : PassiveRaiseSkill).Value);
		}

		internal float GetRaiseSkillBonus(bool doubled)
		{
			return (doubled ? DoubledRaiseSkillBonus : PassiveRaiseSkillBonus).Value;
		}

		internal float GetDamage(string damageType, bool doubled)
		{
			string text = Normalize(damageType);
			foreach (KeyValuePair<string, ConfigEntry<float>> item in doubled ? _doubledDamage : _passiveDamage)
			{
				if (Normalize(item.Key) == text)
				{
					return item.Value.Value;
				}
			}
			return 0f;
		}

		private static string NormalizeSkill(string value)
		{
			string[] skillNames = SkillNames;
			foreach (string text in skillNames)
			{
				if (string.Equals(Normalize(text), Normalize(value), StringComparison.OrdinalIgnoreCase))
				{
					return text;
				}
			}
			return "None";
		}

		private static string Normalize(string value)
		{
			return (value ?? string.Empty).Replace(" ", string.Empty).Replace("_", string.Empty).ToLowerInvariant();
		}

		private static ConfigEntry<string> BindSkill(ConfigFile config, string configSection, string key, Dictionary<string, string> defaults, string neutral)
		{
			//IL_0055: Unknown result type (might be due to invalid IL or missing references)
			//IL_005f: Expected O, but got Unknown
			string key2 = (configSection.EndsWith(".Doubled", StringComparison.Ordinal) ? ("Doubled." + key) : ("Passive." + key));
			string value2;
			string value = (defaults.TryGetValue(key2, out value2) ? value2 : neutral);
			return config.Bind<string>(configSection, key, NormalizeSkill(value), new ConfigDescription("Native Valheim skill slot.", (AcceptableValueBase)(object)new AcceptableValueList<string>(SkillNames), Array.Empty<object>()));
		}

		private ConfigEntry<float> BindFloat(ConfigFile config, string configSection, string key, Dictionary<string, float> defaults, float neutral, string description)
		{
			//IL_009a: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a4: Expected O, but got Unknown
			string key2 = (configSection.EndsWith(".Doubled", StringComparison.Ordinal) ? ("Doubled." + key) : ("Passive." + key));
			float value;
			float num = (defaults.TryGetValue(key2, out value) ? value : neutral);
			GetSafetyBounds(key, out var minimum, out var maximum);
			ConfigEntry<float> entry = config.Bind<float>(configSection, key, num, new ConfigDescription(description, (AcceptableValueBase)(object)new AcceptableValueRange<float>(minimum, maximum), Array.Empty<object>()));
			entry.SettingChanged += delegate
			{
				ClampEntry(entry, configSection, key, minimum, maximum);
			};
			ClampEntry(entry, configSection, key, minimum, maximum);
			return entry;
		}

		private static void GetSafetyBounds(string key, out float minimum, out float maximum)
		{
			minimum = 0f;
			maximum = float.MaxValue;
			if (key.EndsWith("Multiplier", StringComparison.OrdinalIgnoreCase) || key.EndsWith("StaminaUseMultiplier", StringComparison.OrdinalIgnoreCase))
			{
				minimum = 0f;
			}
		}

		private static void ClampEntry(ConfigEntry<float> entry, string section, string key, float minimum, float maximum)
		{
			float value = entry.Value;
			float num = value;
			if (float.IsNaN(num) || float.IsInfinity(num))
			{
				num = minimum;
			}
			else if (num < minimum)
			{
				num = minimum;
			}
			else if (num > maximum)
			{
				num = maximum;
			}
			if (Math.Abs(value - num) > 0.0001f)
			{
				entry.Value = num;
				Plugin.Log.LogWarning((object)$"Clamped config value [{section}] {key} from '{value}' to '{num}'.");
			}
		}
	}
	internal static class TrinketConfigRegistry
	{
		private static readonly List<TrinketDefinition> Definitions = new List<TrinketDefinition>();

		internal static IReadOnlyList<TrinketDefinition> All => Definitions;

		internal static void Initialize(ConfigFile config)
		{
			if (Definitions.Count <= 0)
			{
				Definitions.Add(Create(config, "Bracelets of the Brave", "TrinketBlackDamageHealth", Defaults(Pair("Passive.Skill1Bonus", 20f), Pair("Passive.BluntDamage", 0.15f), Pair("Doubled.Skill1Bonus", 40f), Pair("Doubled.BluntDamage", 0.3f), Pair("Doubled.Duration", 60f)), TextDefaults(Text("Passive.Skill1", "Clubs"), Text("Doubled.Skill1", "Clubs"))));
				Definitions.Add(Create(config, "Brimstone", "TrinketFlametalStaminaHealth", Defaults(Pair("Passive.BluntDamage", 0.15f), Pair("Passive.SlashDamage", 0.15f), Pair("Passive.PierceDamage", 0.15f), Pair("Passive.ChopDamage", 0.15f), Pair("Passive.PickaxeDamage", 0.15f), Pair("Doubled.BluntDamage", 0.3f), Pair("Doubled.SlashDamage", 0.3f), Pair("Doubled.PierceDamage", 0.3f), Pair("Doubled.ChopDamage", 0.3f), Pair("Doubled.PickaxeDamage", 0.3f), Pair("Doubled.Duration", 60f)), TextDefaults()));
				Definitions.Add(Create(config, "Bronze Pendant", "TrinketBronzeStamina", Defaults(Pair("Passive.StaminaRegenMultiplier", 1.25f), Pair("Doubled.StaminaRegenMultiplier", 1.5f), Pair("Doubled.Duration", 60f)), TextDefaults()));
				Definitions.Add(Create(config, "Crystal Heart", "TrinketSilverResist", Defaults(Pair("Doubled.Duration", 50f)), TextDefaults(), "SlightlyResistant", "Resistant"));
				Definitions.Add(Create(config, "Evasion Mantle", "TrinketBlackStamina", Defaults(Pair("Passive.BlockStaminaUseMultiplier", 0.5f), Pair("Passive.TimedBlockBonus", 0.5f), Pair("Passive.Skill1Bonus", 20f), Pair("Doubled.BlockStaminaUseMultiplier", 0f), Pair("Doubled.TimedBlockBonus", 1f), Pair("Doubled.Skill1Bonus", 40f), Pair("Doubled.Duration", 120f)), TextDefaults(Text("Passive.Skill1", "Dodge"), Text("Doubled.Skill1", "Dodge"))));
				Definitions.Add(Create(config, "Fins of Destiny", "TrinketChitinSwim", Defaults(Pair("Passive.SwimSpeed", 0.5f), Pair("Passive.SwimStaminaUseMultiplier", 0.4f), Pair("Doubled.SwimSpeed", 1f), Pair("Doubled.SwimStaminaUseMultiplier", 0.4f), Pair("Doubled.Duration", 120f)), TextDefaults()));
				Definitions.Add(Create(config, "Heart of the Forest", "TrinketBronzeHealth", Defaults(Pair("Passive.HealthRegenMultiplier", 1.25f), Pair("Doubled.HealthRegenMultiplier", 1.5f), Pair("Doubled.Duration", 60f)), TextDefaults()));
				Definitions.Add(Create(config, "Iron Brooch", "TrinketIronHealth", Defaults(Pair("Passive.Armor", 10f), Pair("Doubled.Armor", 20f), Pair("Doubled.Duration", 30f)), TextDefaults()));
				Definitions.Add(Create(config, "Jormundling", "TrinketFlametalEitr", Defaults(Pair("Passive.Skill1Bonus", 20f), Pair("Passive.Skill2Bonus", 20f), Pair("Doubled.Skill1Bonus", 40f), Pair("Doubled.Skill2Bonus", 40f), Pair("Doubled.Duration", 60f)), TextDefaults(Text("Passive.Skill1", "ElementalMagic"), Text("Passive.Skill2", "BloodMagic"), Text("Doubled.Skill1", "ElementalMagic"), Text("Doubled.Skill2", "BloodMagic"))));
				Definitions.Add(Create(config, "Nimble Anklet", "TrinketIronStamina", Defaults(Pair("Passive.Speed", 0.1f), Pair("Doubled.Speed", 0.2f), Pair("Doubled.Duration", 30f)), TextDefaults()));
				Definitions.Add(Create(config, "Neckstabber", "TrinketBloodGoldHealth", Defaults(Pair("Passive.HealthRegenMultiplier", 1.2f), Pair("Passive.RunStaminaUseMultiplier", 0.75f), Pair("Passive.AttackStaminaUseMultiplier", 0.75f)), TextDefaults(), "Normal", "Normal", new string[4] { "TrinketNeckstabber", "Neckstabber", "$item_neckstabber", "$item_trinketneckstabber" }));
				Definitions.Add(Create(config, "Pulsating Earrings", "TrinketCarapaceEitr", Defaults(Pair("Passive.EitrRegenMultiplier", 1.25f), Pair("Doubled.EitrRegenMultiplier", 1.5f), Pair("Doubled.Duration", 60f)), TextDefaults()));
				Definitions.Add(Create(config, "Resounding Shackle", "TrinketScaleStaminaDamage", Defaults(Pair("Passive.SlashDamage", 0.2f), Pair("Doubled.SlashDamage", 0.33f), Pair("Doubled.Duration", 60f)), TextDefaults()));
				Definitions.Add(Create(config, "Wolf Sight", "TrinketSilverDamage", Defaults(Pair("Passive.Skill1Bonus", 20f), Pair("Passive.Skill2Bonus", 20f), Pair("Passive.PierceDamage", 0.15f), Pair("Doubled.Skill1Bonus", 40f), Pair("Doubled.Skill2Bonus", 40f), Pair("Doubled.PierceDamage", 0.3f), Pair("Doubled.Duration", 30f)), TextDefaults(Text("Passive.Skill1", "Bows"), Text("Passive.Skill2", "Sneak"), Text("Doubled.Skill1", "Bows"), Text("Doubled.Skill2", "Sneak"))));
				Definitions.Add(Create(config, "Witch Crown", "TrinketBloodGoldStamina", Defaults(Pair("Passive.HealthRegenMultiplier", 1.33f), Pair("Passive.StaminaRegenMultiplier", 1.33f), Pair("Passive.EitrRegenMultiplier", 1.33f)), TextDefaults(), "Normal", "Normal", new string[4] { "TrinketWitchCrown", "WitchCrown", "$item_witchcrown", "$item_trinketwitchcrown" }));
				config.Save();
			}
		}

		internal static TrinketDefinition? FindByPrefab(string prefabName, string? itemName = null)
		{
			TrinketDefinition trinketDefinition = FindByIdentifier(prefabName);
			if (trinketDefinition != null)
			{
				return trinketDefinition;
			}
			if (itemName == null || itemName.Length == 0)
			{
				return null;
			}
			return FindByIdentifier(itemName);
		}

		private static TrinketDefinition? FindByIdentifier(string identifier)
		{
			string text = NormalizeIdentifier(identifier);
			foreach (TrinketDefinition definition in Definitions)
			{
				if (string.Equals(definition.PrefabName, identifier, StringComparison.OrdinalIgnoreCase))
				{
					return definition;
				}
				foreach (string prefabAlias in definition.PrefabAliases)
				{
					if (string.Equals(text, NormalizeIdentifier(prefabAlias), StringComparison.Ordinal))
					{
						return definition;
					}
				}
				string text2 = NormalizeIdentifier(definition.DisplayName);
				if (text2.Length > 0 && text.Contains(text2))
				{
					return definition;
				}
			}
			return null;
		}

		private static string NormalizeIdentifier(string value)
		{
			if (string.IsNullOrEmpty(value))
			{
				return string.Empty;
			}
			char[] array = new char[value.Length];
			int length = 0;
			foreach (char c in value)
			{
				if (char.IsLetterOrDigit(c))
				{
					array[length++] = char.ToLowerInvariant(c);
				}
			}
			return new string(array, 0, length);
		}

		private static TrinketDefinition Create(ConfigFile config, string section, string prefabName, Dictionary<string, float> numericDefaults, Dictionary<string, string> textDefaults, string passiveResistance = "Normal", string doubledResistance = "Normal", string[]? prefabAliases = null)
		{
			return new TrinketDefinition(config, section, section, prefabName, numericDefaults, textDefaults, passiveResistance, doubledResistance, prefabAliases);
		}

		private static Dictionary<string, float> Defaults(params KeyValuePair<string, float>[] values)
		{
			Dictionary<string, float> dictionary = new Dictionary<string, float>(StringComparer.OrdinalIgnoreCase);
			for (int i = 0; i < values.Length; i++)
			{
				KeyValuePair<string, float> keyValuePair = values[i];
				dictionary[keyValuePair.Key] = keyValuePair.Value;
			}
			return dictionary;
		}

		private static Dictionary<string, string> TextDefaults(params KeyValuePair<string, string>[] values)
		{
			Dictionary<string, string> dictionary = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
			for (int i = 0; i < values.Length; i++)
			{
				KeyValuePair<string, string> keyValuePair = values[i];
				dictionary[keyValuePair.Key] = keyValuePair.Value;
			}
			return dictionary;
		}

		private static KeyValuePair<string, float> Pair(string key, float value)
		{
			return new KeyValuePair<string, float>(key, value);
		}

		private static KeyValuePair<string, string> Text(string key, string value)
		{
			return new KeyValuePair<string, string>(key, value);
		}
	}
	internal static class TrinketEffectManager
	{
		private sealed class EquippedTrinket
		{
			internal TrinketDefinition Definition { get; }

			internal ItemData Item { get; }

			internal List<string> StatusEffectNames { get; }

			internal EquippedTrinket(TrinketDefinition definition, ItemData item, List<string> statusEffectNames)
			{
				Definition = definition;
				Item = item;
				StatusEffectNames = statusEffectNames;
			}
		}

		private sealed class PlayerState
		{
			internal string Signature = string.Empty;

			internal float NextRefresh;

			internal float DoubledUntil;

			internal bool AdrenalineWasFull;

			internal bool HasAppliedDoubledState;

			internal bool AppliedDoubledState;

			internal bool LoggedMissingStatusEffect;

			internal bool SaveSuspended;

			internal bool ForcedRefreshInProgress;

			internal object? GlobalAdrenalineStatusEffect;

			internal readonly List<object> AddedPassiveStatusEffects = new List<object>();

			internal readonly Dictionary<string, object> PassiveStatusEffectsByPrefab = new Dictionary<string, object>(StringComparer.OrdinalIgnoreCase);
		}

		private static readonly Dictionary<int, PlayerState> States = new Dictionary<int, PlayerState>();

		private static readonly Dictionary<string, float> NativeNeutralValues = new Dictionary<string, float>(StringComparer.Ordinal);

		private static readonly BindingFlags AnyInstance = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic;

		private static readonly BindingFlags AnyMember = BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic;

		internal static void OnPlayerSpawned(Player player)
		{
			PlayerState state = GetState(player);
			RemoveAddedPassiveStatusEffects(player, state);
			state.Signature = string.Empty;
			state.NextRefresh = Time.time + 0.5f;
			state.DoubledUntil = 0f;
			state.AdrenalineWasFull = false;
			state.HasAppliedDoubledState = false;
			state.AppliedDoubledState = false;
			state.LoggedMissingStatusEffect = false;
			state.SaveSuspended = false;
		}

		internal static void OnDoublingSettingChanged(bool enabled)
		{
			foreach (PlayerState value in States.Values)
			{
				value.DoubledUntil = 0f;
				value.AdrenalineWasFull = false;
				value.NextRefresh = 0f;
				if (!enabled && value.HasAppliedDoubledState)
				{
					value.AppliedDoubledState = true;
				}
			}
			Plugin.Log.LogInfo((object)("Adrenaline doubling " + (enabled ? "enabled" : "disabled") + "; active trinket effects will be refreshed from the appropriate config section."));
		}

		internal static void Tick(Player player)
		{
			PlayerState state = GetState(player);
			if (!state.SaveSuspended && !state.ForcedRefreshInProgress && !(Time.time < state.NextRefresh))
			{
				state.NextRefresh = Time.time + 0.25f;
				Refresh(player);
			}
		}

		internal static void RefreshAfterEquipmentStatusEffects(Player player)
		{
			if ((Object)(object)player == (Object)null)
			{
				return;
			}
			PlayerState state = GetState(player);
			if (state.SaveSuspended || state.ForcedRefreshInProgress)
			{
				return;
			}
			state.ForcedRefreshInProgress = true;
			try
			{
				state.NextRefresh = 0f;
				Refresh(player);
				state.NextRefresh = Time.time + 0.25f;
			}
			finally
			{
				state.ForcedRefreshInProgress = false;
			}
		}

		private static void Refresh(Player player)
		{
			if ((Object)(object)player == (Object)null)
			{
				return;
			}
			PlayerState state = GetState(player);
			if (!state.SaveSuspended && IsPlayerReady(player))
			{
				List<EquippedTrinket> equipped = FindEquippedTrinkets(player);
				string text = BuildSignature(equipped);
				if (!string.Equals(text, state.Signature, StringComparison.Ordinal))
				{
					Plugin.Log.LogInfo((object)("Trinket equipment changed: '" + state.Signature + "' -> '" + text + "'. Detected: " + DescribeEquippedTrinkets(equipped)));
					state.Signature = text;
					state.LoggedMissingStatusEffect = false;
					RemoveAddedPassiveStatusEffects(player, state);
				}
				bool value = Plugin.DoublingOnAdrenaline.Value;
				if (!value)
				{
					state.DoubledUntil = 0f;
					state.AdrenalineWasFull = false;
				}
				bool flag = IsAdrenalineFull(player);
				if (value && flag && !state.AdrenalineWasFull)
				{
					float longestDuration = GetLongestDuration(equipped);
					state.DoubledUntil = Time.time + longestDuration;
					Plugin.Log.LogInfo((object)$"Adrenaline doubling started for {longestDuration:0.##} seconds.");
				}
				state.AdrenalineWasFull = flag;
				bool flag2 = value && state.DoubledUntil > Time.time;
				if (state.HasAppliedDoubledState && state.AppliedDoubledState != flag2)
				{
					RemoveAddedPassiveStatusEffects(player, state);
				}
				EnsurePassiveStatusEffects(player, equipped, flag2, state);
				ApplyActiveStatusEffects(player, equipped, flag2, state);
				ApplyGlobalAdrenalineModifier(player, state);
				state.HasAppliedDoubledState = true;
				state.AppliedDoubledState = flag2;
				if (!flag2 && state.DoubledUntil > 0f && Plugin.DebugLogging)
				{
					Plugin.Log.LogInfo((object)"Adrenaline doubling expired; passive values restored.");
					state.DoubledUntil = 0f;
				}
			}
		}

		private static bool IsPlayerReady(Player player)
		{
			object memberValue = GetMemberValue(player, "m_dead");
			bool flag = default(bool);
			int num;
			if (memberValue is bool)
			{
				flag = (bool)memberValue;
				num = 1;
			}
			else
			{
				num = 0;
			}
			if (((uint)num & (flag ? 1u : 0u)) != 0)
			{
				return false;
			}
			float? num2 = InvokeNumberGetter(player, "GetHealth");
			if (num2.HasValue)
			{
				if (num2.Value > 0f && !float.IsNaN(num2.Value))
				{
					return !float.IsInfinity(num2.Value);
				}
				return false;
			}
			return true;
		}

		private static List<EquippedTrinket> FindEquippedTrinkets(Player player)
		{
			List<EquippedTrinket> list = new List<EquippedTrinket>();
			Inventory inventory = ((Humanoid)player).GetInventory();
			if (inventory == null)
			{
				return list;
			}
			foreach (ItemData allItem in inventory.GetAllItems())
			{
				if (allItem != null && IsEquipped(allItem) && !((Object)(object)allItem.m_dropPrefab == (Object)null))
				{
					string itemName = ((allItem.m_shared == null) ? string.Empty : allItem.m_shared.m_name);
					TrinketDefinition trinketDefinition = TrinketConfigRegistry.FindByPrefab(((Object)allItem.m_dropPrefab).name, itemName);
					if (trinketDefinition != null)
					{
						list.Add(new EquippedTrinket(trinketDefinition, allItem, GetStatusEffectNames(allItem)));
					}
				}
			}
			return list;
		}

		private static string DescribeEquippedTrinkets(List<EquippedTrinket> equipped)
		{
			if (equipped.Count == 0)
			{
				return "<none>";
			}
			List<string> list = new List<string>();
			foreach (EquippedTrinket item in equipped)
			{
				TrinketDefinition definition = item.Definition;
				list.Add(definition.PrefabName + $"(armor={definition.PassiveArmor.Value:0.##}," + $"speed={definition.PassiveSpeed.Value:0.##})");
			}
			return string.Join(", ", list);
		}

		private static string BuildSignature(List<EquippedTrinket> equipped)
		{
			if (equipped.Count == 0)
			{
				return string.Empty;
			}
			List<string> list = new List<string>();
			foreach (EquippedTrinket item in equipped)
			{
				list.Add(item.Definition.PrefabName);
			}
			list.Sort(StringComparer.OrdinalIgnoreCase);
			return string.Join("|", list);
		}

		private static void ApplyActiveStatusEffects(Player player, List<EquippedTrinket> equipped, bool doubled, PlayerState state)
		{
			object obj = InvokeNoArgumentMethod(player, "GetSEMan");
			if (obj == null)
			{
				return;
			}
			List<object> activeStatusEffects = GetActiveStatusEffects(player, obj);
			foreach (EquippedTrinket item in equipped)
			{
				bool flag = false;
				foreach (object item2 in activeStatusEffects)
				{
					if (MatchesStatusEffect(item.StatusEffectNames, item2))
					{
						ApplyStatusEffectFields(item2, item.Definition, doubled);
						flag = true;
						if (Plugin.DebugLogging)
						{
							Plugin.Log.LogDebug((object)("Applied " + (doubled ? "doubled" : "passive") + " values to status effect '" + GetObjectName(item2) + "' for trinket '" + item.Definition.PrefabName + "'."));
						}
					}
				}
				if (!flag && !state.LoggedMissingStatusEffect && Plugin.DebugLogging)
				{
					Plugin.Log.LogDebug((object)("No active status effect matched configured trinket '" + item.Definition.PrefabName + "'. The item was found and equipped, but its game status effect was not active yet."));
					state.LoggedMissingStatusEffect = true;
				}
			}
		}

		private static void EnsurePassiveStatusEffects(Player player, List<EquippedTrinket> equipped, bool doubled, PlayerState state)
		{
			object obj = InvokeNoArgumentMethod(player, "GetSEMan");
			if (obj == null)
			{
				return;
			}
			List<object> activeStatusEffects = GetActiveStatusEffects(player, obj);
			foreach (EquippedTrinket item in equipped)
			{
				if (state.PassiveStatusEffectsByPrefab.TryGetValue(item.Definition.PrefabName, out object value))
				{
					if (IsUsableUnityObject(value))
					{
						ApplyStatusEffectFields(value, item.Definition, doubled);
						if (!activeStatusEffects.Contains(value))
						{
							activeStatusEffects.Add(value);
						}
						continue;
					}
					state.PassiveStatusEffectsByPrefab.Remove(item.Definition.PrefabName);
				}
				AddStatusEffectIdentifier(item.StatusEffectNames, GetSyntheticStatusEffectName(item.Definition.PrefabName));
				if (HasMatchingStatusEffect(activeStatusEffects, item.StatusEffectNames))
				{
					continue;
				}
				object trinketStatusEffectTemplate = GetTrinketStatusEffectTemplate(item);
				if (trinketStatusEffectTemplate != null)
				{
					AddStatusEffectIdentifiers(item.StatusEffectNames, trinketStatusEffectTemplate);
					if (HasMatchingStatusEffect(activeStatusEffects, item.StatusEffectNames))
					{
						continue;
					}
				}
				bool flag = trinketStatusEffectTemplate == null;
				object obj2 = ((!flag) ? CloneStatusEffect(trinketStatusEffectTemplate) : CreateSyntheticStatusEffect(item));
				if (obj2 == null)
				{
					Plugin.Log.LogWarning((object)("Could not create a passive status effect for '" + item.Definition.PrefabName + "'."));
					continue;
				}
				ApplyStatusEffectFields(obj2, item.Definition, doubled);
				ApplyFallbackStatusEffectIcon(obj2, item.Item);
				if (Plugin.DebugLogging)
				{
					string memberText = GetMemberText(obj2, "m_dodgeStaminaUseModifier");
					string memberText2 = GetMemberText(obj2, "m_fallDamageModifier");
					string memberText3 = GetMemberText(obj2, "m_adrenalineModifier");
					Plugin.Log.LogDebug((object)("Dodge stamina field for '" + item.Definition.PrefabName + "': " + $"configured multiplier={(doubled ? item.Definition.DoubledDodgeStaminaUse.Value : item.Definition.PassiveDodgeStaminaUse.Value):0.##}; " + "native m_dodgeStaminaUseModifier=" + memberText + "."));
					Plugin.Log.LogDebug((object)("Neutral-sensitive fields for '" + item.Definition.PrefabName + "': " + $"configured fall multiplier={(doubled ? item.Definition.DoubledFallDamage.Value : item.Definition.PassiveFallDamage.Value):0.##}; " + "native m_fallDamageModifier=" + memberText2 + "; native m_adrenalineModifier=" + memberText3 + "."));
				}
				if (!AddStatusEffect(obj, obj2))
				{
					Plugin.Log.LogWarning((object)("Could not add the passive status effect for '" + item.Definition.PrefabName + "'."));
					DestroyClonedStatusEffect(obj2);
					continue;
				}
				state.AddedPassiveStatusEffects.Add(obj2);
				state.PassiveStatusEffectsByPrefab[item.Definition.PrefabName] = obj2;
				activeStatusEffects.Add(obj2);
				if (Plugin.DebugLogging)
				{
					Plugin.Log.LogDebug((object)("Added passive status effect '" + GetObjectName(obj2) + "' for '" + item.Definition.PrefabName + "' (" + (flag ? "native fallback" : "item template") + ") with native passive values."));
				}
			}
		}

		internal static void BeforePlayerSave(Player player)
		{
			if (!((Object)(object)player == (Object)null))
			{
				PlayerState state = GetState(player);
				if (!state.SaveSuspended)
				{
					state.SaveSuspended = true;
					RemoveAddedPassiveStatusEffects(player, state);
				}
			}
		}

		internal static void AfterPlayerSave(Player player)
		{
			if ((Object)(object)player == (Object)null)
			{
				return;
			}
			PlayerState state = GetState(player);
			if (state.SaveSuspended)
			{
				state.SaveSuspended = false;
				state.NextRefresh = Time.time + 0.1f;
				if (IsPlayerReady(player))
				{
					Refresh(player);
				}
			}
		}

		private static bool HasMatchingStatusEffect(List<object> activeEffects, List<string> identifiers)
		{
			foreach (object activeEffect in activeEffects)
			{
				if (MatchesStatusEffect(identifiers, activeEffect))
				{
					return true;
				}
			}
			return false;
		}

		private static void RemoveAddedPassiveStatusEffects(Player player, PlayerState state)
		{
			if (state.AddedPassiveStatusEffects.Count == 0)
			{
				state.PassiveStatusEffectsByPrefab.Clear();
				state.GlobalAdrenalineStatusEffect = null;
				return;
			}
			object obj = InvokeNoArgumentMethod(player, "GetSEMan");
			foreach (object addedPassiveStatusEffect in state.AddedPassiveStatusEffects)
			{
				if (obj != null)
				{
					RemoveStatusEffect(obj, addedPassiveStatusEffect);
				}
				DestroyClonedStatusEffect(addedPassiveStatusEffect);
			}
			state.AddedPassiveStatusEffects.Clear();
			state.PassiveStatusEffectsByPrefab.Clear();
			state.GlobalAdrenalineStatusEffect = null;
		}

		private static object? GetTrinketStatusEffectTemplate(EquippedTrinket trinket)
		{
			object shared = trinket.Item.m_shared;
			string[] array = new string[3] { "m_equipStatusEffect", "m_setStatusEffect", "m_attackStatusEffect" };
			foreach (string memberName in array)
			{
				object obj = GetMemberValue(shared, memberName) ?? GetMemberValue(trinket.Item, memberName);
				if (IsUsableUnityObject(obj))
				{
					return obj;
				}
			}
			object obj2 = FindStatusEffectInObjectDb(trinket.Definition.PrefabName);
			if (IsUsableUnityObject(obj2))
			{
				if (Plugin.DebugLogging)
				{
					Plugin.Log.LogDebug((object)("Found status-effect template for '" + trinket.Definition.PrefabName + "' by prefab name: '" + GetObjectName(obj2) + "'."));
				}
				return obj2;
			}
			foreach (string statusEffectName in trinket.StatusEffectNames)
			{
				object obj3 = FindStatusEffectInObjectDb(statusEffectName);
				if (IsUsableUnityObject(obj3))
				{
					return obj3;
				}
			}
			return null;
		}

		private static string GetSyntheticStatusEffectName(string prefabName)
		{
			return "TrinketModifiers_" + prefabName;
		}

		private static object? CreateSyntheticStatusEffect(EquippedTrinket trinket)
		{
			Type type = typeof(Player).Assembly.GetType("SE_Stats");
			if (type == null || !typeof(ScriptableObject).IsAssignableFrom(type))
			{
				return null;
			}
			try
			{
				ScriptableObject obj = ScriptableObject.CreateInstance(type);
				string syntheticStatusEffectName = GetSyntheticStatusEffectName(trinket.Definition.PrefabName);
				((Object)obj).name = syntheticStatusEffectName;
				SetText(obj, "m_name", trinket.Definition.DisplayName);
				SetText(obj, "m_tooltip", "Active while the trinket is equipped.");
				ApplyFallbackStatusEffectIcon(obj, trinket.Item);
				return obj;
			}
			catch (Exception ex)
			{
				if (Plugin.DebugLogging)
				{
					Plugin.Log.LogDebug((object)("Creating synthetic status effect for '" + trinket.Definition.PrefabName + "' failed: " + ex.GetBaseException().Message));
				}
				return null;
			}
		}

		private static object? CreateGlobalAdrenalineStatusEffect()
		{
			Type type = typeof(Player).Assembly.GetType("SE_Stats");
			if (type == null || !typeof(ScriptableObject).IsAssignableFrom(type))
			{
				return null;
			}
			try
			{
				ScriptableObject obj = ScriptableObject.CreateInstance(type);
				((Object)obj).name = "TrinketModifiers_GlobalAdrenaline";
				SetText(obj, "m_name", "Trinket Modifiers");
				SetText(obj, "m_tooltip", "Global adrenaline gain modifier.");
				SetNumber(obj, "m_ttl", 0f);
				SetNumber(obj, "m_adrenalineModifier", TranslateMultiplier("m_adrenalineModifier", Plugin.AdrenalineGainMultiplier.Value, 0f));
				return obj;
			}
			catch (Exception ex)
			{
				if (Plugin.DebugLogging)
				{
					Plugin.Log.LogDebug((object)("Creating the global adrenaline status effect failed: " + ex.GetBaseException().Message));
				}
				return null;
			}
		}

		private static void ApplyFallbackStatusEffectIcon(object effect, ItemData item)
		{
			if (GetMemberValue(effect, "m_icon") == null)
			{
				Sprite trinketIcon = GetTrinketIcon(item);
				if ((Object)(object)trinketIcon != (Object)null)
				{
					SetObject(effect, "m_icon", trinketIcon);
				}
			}
			SetBooleanOrNumber(effect, "m_iconAlways", value: true);
		}

		private static Sprite? GetTrinketIcon(ItemData item)
		{
			object? obj = InvokeNoArgumentMethod(item, "GetIcon");
			Sprite val = (Sprite)((obj is Sprite) ? obj : null);
			if (val != null && (Object)(object)val != (Object)null)
			{
				return val;
			}
			object shared = item.m_shared;
			object? memberValue = GetMemberValue(shared, "m_icon");
			Sprite val2 = (Sprite)((memberValue is Sprite) ? memberValue : null);
			if (val2 != null && (Object)(object)val2 != (Object)null)
			{
				return val2;
			}
			if (GetMemberValue(shared, "m_icons") is IEnumerable enumerable)
			{
				foreach (object item2 in enumerable)
				{
					Sprite val3 = (Sprite)((item2 is Sprite) ? item2 : null);
					if (val3 != null && (Object)(object)val3 != (Object)null)
					{
						return val3;
					}
				}
			}
			object? memberValue2 = GetMemberValue(item, "m_icon");
			Sprite val4 = (Sprite)((memberValue2 is Sprite) ? memberValue2 : null);
			if (val4 == null || !((Object)(object)val4 != (Object)null))
			{
				return null;
			}
			return val4;
		}

		private static object? FindStatusEffectInObjectDb(string identifier)
		{
			Type type = typeof(Player).Assembly.GetType("ObjectDB");
			if (type == null)
			{
				return null;
			}
			object staticMemberValue = GetStaticMemberValue(type, "instance");
			if (staticMemberValue == null)
			{
				return null;
			}
			MethodInfo[] methods = staticMemberValue.GetType().GetMethods(AnyInstance);
			foreach (MethodInfo methodInfo in methods)
			{
				if (!string.Equals(methodInfo.Name, "GetStatusEffect", StringComparison.Ordinal) || methodInfo.ContainsGenericParameters)
				{
					continue;
				}
				ParameterInfo[] parameters = methodInfo.GetParameters();
				if (parameters.Length != 1)
				{
					continue;
				}
				object obj = null;
				object converted;
				if (parameters[0].ParameterType == typeof(string))
				{
					obj = identifier;
				}
				else if (TryConvertIdentifier(identifier, parameters[0].ParameterType, out converted))
				{
					obj = converted;
				}
				if (obj == null && parameters[0].ParameterType.IsValueType)
				{
					continue;
				}
				try
				{
					object obj2 = methodInfo.Invoke(staticMemberValue, new object[1] { obj });
					if (IsUsableUnityObject(obj2))
					{
						return obj2;
					}
				}
				catch
				{
				}
			}
			string[] array = new string[4] { "m_StatusEffects", "m_statusEffects", "m_statusEffectPrefabs", "m_StatusEffectPrefabs" };
			foreach (string memberName in array)
			{
				if (!(GetMemberValue(staticMemberValue, memberName) is IEnumerable enumerable))
				{
					continue;
				}
				foreach (object item in enumerable)
				{
					if (item != null && MatchesIdentifier(item, identifier) && IsUsableUnityObject(item))
					{
						return item;
					}
				}
			}
			return null;
		}

		private static bool MatchesIdentifier(object candidate, string identifier)
		{
			List<string> list = new List<string>();
			AddStatusEffectIdentifiers(list, candidate);
			foreach (string item in list)
			{
				if (ContainsName(new List<string> { identifier }, item))
				{
					return true;
				}
			}
			return false;
		}

		private static object? CloneStatusEffect(object template)
		{
			Object val = (Object)((template is Object) ? template : null);
			if (val != null)
			{
				if (val == (Object)null)
				{
					return null;
				}
				Object val2 = Object.Instantiate(val);
				if (!(val2 == (Object)null))
				{
					return val2;
				}
				return null;
			}
			return template;
		}

		private static void DestroyClonedStatusEffect(object effect)
		{
			Object val = (Object)((effect is Object) ? effect : null);
			if (val != null && val != (Object)null)
			{
				Object.Destroy(val);
			}
		}

		private static bool AddStatusEffect(object seMan, object effect)
		{
			return InvokeWithFirstArgument(seMan, "AddStatusEffect", effect);
		}

		private static bool RemoveStatusEffect(object seMan, object effect)
		{
			if (InvokeWithFirstArgument(seMan, "RemoveStatusEffect", effect))
			{
				return true;
			}
			object memberValue = GetMemberValue(effect, "m_hash");
			if (memberValue != null)
			{
				return InvokeWithFirstArgument(seMan, "RemoveStatusEffect", memberValue);
			}
			return false;
		}

		private static bool InvokeWithFirstArgument(object target, string methodName, object firstArgument)
		{
			MethodInfo[] methods = target.GetType().GetMethods(AnyInstance);
			foreach (MethodInfo methodInfo in methods)
			{
				if (!string.Equals(methodInfo.Name, methodName, StringComparison.Ordinal) || methodInfo.ContainsGenericParameters || !TryBuildArguments(methodInfo.GetParameters(), firstArgument, out object[] arguments))
				{
					continue;
				}
				try
				{
					methodInfo.Invoke(target, arguments);
					return true;
				}
				catch (Exception ex)
				{
					if (Plugin.DebugLogging)
					{
						Plugin.Log.LogDebug((object)("Calling " + methodName + " failed: " + ex.GetBaseException().Message));
					}
				}
			}
			return false;
		}

		private static bool TryBuildArguments(ParameterInfo[] parameters, object firstArgument, out object?[] arguments)
		{
			arguments = new object[parameters.Length];
			if (parameters.Length == 0 || !CanAssign(parameters[0].ParameterType, firstArgument))
			{
				return false;
			}
			arguments[0] = firstArgument;
			for (int i = 1; i < parameters.Length; i++)
			{
				ParameterInfo parameterInfo = parameters[i];
				if (parameterInfo.HasDefaultValue && parameterInfo.DefaultValue != null && parameterInfo.DefaultValue != DBNull.Value && parameterInfo.DefaultValue != Missing.Value)
				{
					arguments[i] = parameterInfo.DefaultValue;
				}
				else if (parameterInfo.ParameterType == typeof(bool))
				{
					arguments[i] = false;
				}
				else if (parameterInfo.ParameterType.IsValueType)
				{
					arguments[i] = Activator.CreateInstance(parameterInfo.ParameterType);
				}
				else
				{
					arguments[i] = null;
				}
			}
			return true;
		}

		private static bool CanAssign(Type parameterType, object value)
		{
			if (parameterType.IsInstanceOfType(value))
			{
				return true;
			}
			return parameterType == typeof(object);
		}

		private static bool TryConvertIdentifier(string identifier, Type targetType, out object? converted)
		{
			if (targetType == typeof(int) && int.TryParse(identifier, out var result))
			{
				converted = result;
				return true;
			}
			if (targetType == typeof(uint) && uint.TryParse(identifier, out var result2))
			{
				converted = result2;
				return true;
			}
			converted = null;
			return false;
		}

		private static void ApplyStatusEffectFields(object effect, TrinketDefinition definition, bool doubled)
		{
			float value = (doubled ? definition.DoubledHealthRegen.Value : definition.PassiveHealthRegen.Value);
			float value2 = (doubled ? definition.DoubledStaminaRegen.Value : definition.PassiveStaminaRegen.Value);
			float value3 = (doubled ? definition.DoubledEitrRegen.Value : definition.PassiveEitrRegen.Value);
			float value4 = (doubled ? definition.DoubledArmor.Value : definition.PassiveArmor.Value);
			float value5 = (doubled ? definition.DoubledSpeed.Value : definition.PassiveSpeed.Value);
			float value6 = (doubled ? definition.DoubledSwimSpeed.Value : definition.PassiveSwimSpeed.Value);
			float value7 = (doubled ? definition.DoubledJump.Value : definition.PassiveJump.Value);
			float configuredMultiplier = (doubled ? definition.DoubledFallDamage.Value : definition.PassiveFallDamage.Value);
			float configuredMultiplier2 = (doubled ? definition.DoubledNoise.Value : definition.PassiveNoise.Value);
			float configuredMultiplier3 = (doubled ? definition.DoubledStealth.Value : definition.PassiveStealth.Value);
			float value8 = (doubled ? definition.DoubledMaxCarryWeight.Value : definition.PassiveMaxCarryWeight.Value);
			float value9 = (doubled ? definition.DoubledTimedBlockBonus.Value : definition.PassiveTimedBlockBonus.Value);
			float configuredMultiplier4 = (doubled ? definition.DoubledStagger.Value : definition.PassiveStagger.Value);
			float value10 = (doubled ? definition.DoubledDamageModifier.Value : definition.PassiveDamageModifier.Value);
			float configuredMultiplier5 = (doubled ? definition.DoubledRunStaminaUse.Value : definition.PassiveRunStaminaUse.Value);
			float configuredMultiplier6 = (doubled ? definition.DoubledJumpStaminaUse.Value : definition.PassiveJumpStaminaUse.Value);
			float configuredMultiplier7 = (doubled ? definition.DoubledAttackStaminaUse.Value : definition.PassiveAttackStaminaUse.Value);
			float configuredMultiplier8 = (doubled ? definition.DoubledBlockStaminaUse.Value : definition.PassiveBlockStaminaUse.Value);
			float configuredMultiplier9 = (doubled ? definition.DoubledDodgeStaminaUse.Value : definition.PassiveDodgeStaminaUse.Value);
			float configuredMultiplier10 = (doubled ? definition.DoubledSwimStaminaUse.Value : definition.PassiveSwimStaminaUse.Value);
			float configuredMultiplier11 = (doubled ? definition.DoubledHomeItemStaminaUse.Value : definition.PassiveHomeItemStaminaUse.Value);
			float configuredMultiplier12 = (doubled ? definition.DoubledSneakStaminaUse.Value : definition.PassiveSneakStaminaUse.Value);
			SetNumber(effect, "m_ttl", 0f);
			SetNumber(effect, "m_healthUpFront", 0f);
			SetNumber(effect, "m_staminaUpFront", 0f);
			SetNumber(effect, "m_eitrUpFront", 0f);
			SetNumber(effect, "m_healthRegenMultiplier", value);
			SetNumber(effect, "m_staminaRegenMultiplier", value2);
			SetNumber(effect, "m_eitrRegenMultiplier", value3);
			SetNumber(effect, "m_addArmor", value4);
			SetNumber(effect, "m_speedModifier", value5);
			SetNumber(effect, "m_swimSpeedModifier", value6);
			SetNumber(effect, "m_jumpModifier", value7);
			SetNumber(effect, "m_fallDamageModifier", TranslateMultiplier("m_fallDamageModifier", configuredMultiplier, 1f));
			SetNumber(effect, "m_noiseModifier", TranslateMultiplier("m_noiseModifier", configuredMultiplier2, 0f));
			SetNumber(effect, "m_stealthModifier", TranslateMultiplier("m_stealthModifier", configuredMultiplier3, 0f));
			SetNumber(effect, "m_addMaxCarryWeight", value8);
			SetNumber(effect, "m_timedBlockBonus", value9);
			SetNumber(effect, "m_staggerModifier", TranslateMultiplier("m_staggerModifier", configuredMultiplier4, 0f));
			SetNumber(effect, "m_damageModifier", value10);
			SetNumber(effect, "m_runStaminaUseModifier", TranslateMultiplier("m_runStaminaUseModifier", configuredMultiplier5, 0f));
			SetNumber(effect, "m_runStaminaDrainModifier", TranslateMultiplier("m_runStaminaDrainModifier", configuredMultiplier5, 0f));
			SetNumber(effect, "m_jumpStaminaUseModifier", TranslateMultiplier("m_jumpStaminaUseModifier", configuredMultiplier6, 0f));
			SetNumber(effect, "m_attackStaminaUseModifier", TranslateMultiplier("m_attackStaminaUseModifier", configuredMultiplier7, 0f));
			SetNumber(effect, "m_blockStaminaUseModifier", TranslateMultiplier("m_blockStaminaUseModifier", configuredMultiplier8, 0f));
			SetNumber(effect, "m_dodgeStaminaUseModifier", TranslateMultiplier("m_dodgeStaminaUseModifier", configuredMultiplier9, 0f));
			SetNumber(effect, "m_swimStaminaUseModifier", TranslateMultiplier("m_swimStaminaUseModifier", configuredMultiplier10, 0f));
			SetNumber(effect, "m_homeItemStaminaUseModifier", TranslateMultiplier("m_homeItemStaminaUseModifier", configuredMultiplier11, 0f));
			SetNumber(effect, "m_sneakStaminaUseModifier", TranslateMultiplier("m_sneakStaminaUseModifier", configuredMultiplier12, 0f));
			SetNumber(effect, "m_adrenalineModifier", GetNativeNeutralValue("m_adrenalineModifier", 0f));
			ApplyNativeSkillModifiers(effect, definition, doubled);
			ApplyNativeDamageModifiers(effect, definition, doubled);
			string resistance = (doubled ? definition.DoubledResistance.Value : definition.PassiveResistance.Value);
			ApplyResistance(effect, resistance);
		}

		private static void ApplyNativeSkillModifiers(object effect, TrinketDefinition definition, bool doubled)
		{
			string skillName = definition.GetSkillName(1, doubled);
			string skillName2 = definition.GetSkillName(2, doubled);
			string raiseSkillName = definition.GetRaiseSkillName(doubled);
			SetEnumOrString(effect, "m_skillLevel", skillName);
			SetNumber(effect, "m_skillLevelModifier", (skillName == "None") ? 0f : definition.GetSkillBonus(1, doubled));
			SetEnumOrString(effect, "m_skillLevel2", skillName2);
			SetNumber(effect, "m_skillLevelModifier2", (skillName2 == "None") ? 0f : definition.GetSkillBonus(2, doubled));
			SetEnumOrString(effect, "m_raiseSkill", raiseSkillName);
			SetNumber(effect, "m_raiseSkillModifier", (raiseSkillName == "None") ? 1f : definition.GetRaiseSkillBonus(doubled));
		}

		private static void ApplyNativeDamageModifiers(object effect, TrinketDefinition definition, bool doubled)
		{
			object memberValue = GetMemberValue(effect, "m_percentigeDamageModifiers");
			if (memberValue != null)
			{
				string[] damageTypes = TrinketDefinition.DamageTypes;
				foreach (string text in damageTypes)
				{
					SetNumber(memberValue, "m_" + text.ToLowerInvariant(), definition.GetDamage(text, doubled));
				}
				SetNumber(memberValue, "m_nonPlayer", 0f);
				SetNumber(effect, "m_damageModifier", doubled ? definition.DoubledDamageModifier.Value : definition.PassiveDamageModifier.Value);
				SetFieldValue(effect, "m_percentigeDamageModifiers", memberValue);
			}
		}

		private static void ApplyResistance(object effect, string resistance)
		{
			if (!(GetMemberValue(effect, "m_mods") is IEnumerable enumerable))
			{
				return;
			}
			foreach (object item in enumerable)
			{
				if (item != null)
				{
					SetEnumOrString(item, "m_modifier", resistance);
				}
			}
		}

		private static void ApplyGlobalAdrenalineModifier(Player player, PlayerState state)
		{
			object obj = InvokeNoArgumentMethod(player, "GetSEMan");
			if (obj == null)
			{
				return;
			}
			float value = Plugin.AdrenalineGainMultiplier.Value;
			if (Math.Abs(value - 1f) <= 0.0001f)
			{
				if (state.GlobalAdrenalineStatusEffect != null)
				{
					object globalAdrenalineStatusEffect = state.GlobalAdrenalineStatusEffect;
					RemoveStatusEffect(obj, globalAdrenalineStatusEffect);
					DestroyClonedStatusEffect(globalAdrenalineStatusEffect);
					state.AddedPassiveStatusEffects.Remove(globalAdrenalineStatusEffect);
					state.GlobalAdrenalineStatusEffect = null;
				}
				return;
			}
			if (state.GlobalAdrenalineStatusEffect == null)
			{
				object obj2 = CreateGlobalAdrenalineStatusEffect();
				if (obj2 == null || !AddStatusEffect(obj, obj2))
				{
					if (obj2 != null)
					{
						DestroyClonedStatusEffect(obj2);
					}
					return;
				}
				state.GlobalAdrenalineStatusEffect = obj2;
				state.AddedPassiveStatusEffects.Add(obj2);
			}
			SetNumber(state.GlobalAdrenalineStatusEffect, "m_adrenalineModifier", TranslateMultiplier("m_adrenalineModifier", value, 0f));
		}

		private static bool IsAdrenalineFull(Player player)
		{
			float? num = InvokeNumberGetter(player, "GetAdrenaline");
			float? num2 = InvokeNumberGetter(player, "GetMaxAdrenaline");
			if (num.HasValue && num2.HasValue && num2.Value > 0f)
			{
				return num.Value >= num2.Value - 0.01f;
			}
			return false;
		}

		private static float GetLongestDuration(List<EquippedTrinket> equipped)
		{
			float num = 0f;
			foreach (EquippedTrinket item in equipped)
			{
				num = Math.Max(num, item.Definition.Duration.Value);
			}
			return num;
		}

		private static List<object> GetActiveStatusEffects(Player player, object seMan)
		{
			List<object> result = new List<object>();
			AddEnumerableObjects(result, GetMemberValue(player, "m_equipmentStatusEffects"));
			if (InvokeNoArgumentMethod(seMan, "GetStatusEffects") is IEnumerable values)
			{
				AddEnumerableObjects(result, values);
			}
			object memberValue = GetMemberValue(seMan, "m_statusEffects");
			AddEnumerableObjects(result, memberValue);
			return result;
		}

		private static void AddEnumerableObjects(List<object> result, object? values)
		{
			if (!(values is IEnumerable enumerable))
			{
				return;
			}
			foreach (object item in enumerable)
			{
				if (item != null && !result.Contains(item))
				{
					result.Add(item);
				}
			}
		}

		private static List<string> GetStatusEffectNames(ItemData item)
		{
			List<string> list = new List<string>();
			object shared = item.m_shared;
			string[] array = new string[3] { "m_equipStatusEffect", "m_setStatusEffect", "m_attackStatusEffect" };
			foreach (string memberName in array)
			{
				AddStatusEffectIdentifiers(list, GetMemberValue(shared, memberName));
				AddStatusEffectIdentifiers(list, GetMemberValue(item, memberName));
			}
			return list;
		}

		private static void AddStatusEffectIdentifiers(List<string> names, object? value)
		{
			if (value != null)
			{
				AddStatusEffectIdentifier(names, GetMemberValue(value, "m_name"));
				AddStatusEffectIdentifier(names, GetMemberValue(value, "m_hash"));
				Object val = (Object)((value is Object) ? value : null);
				if (val != null && val != (Object)null)
				{
					AddStatusEffectIdentifier(names, val.name);
				}
			}
		}

		private static void AddStatusEffectIdentifier(List<string> names, object? value)
		{
			if (value != null)
			{
				string text = value.ToString() ?? string.Empty;
				if (!string.IsNullOrWhiteSpace(text) && !ContainsName(names, text))
				{
					names.Add(text);
				}
			}
		}

		private static bool MatchesStatusEffect(List<string> configuredIdentifiers, object effect)
		{
			List<string> list = new List<string>();
			AddStatusEffectIdentifiers(list, effect);
			foreach (string item in list)
			{
				if (ContainsName(configuredIdentifiers, item))
				{
					return true;
				}
			}
			return false;
		}

		private static bool ContainsName(List<string> names, string candidate)
		{
			string b = NormalizeStatusIdentifier(candidate);
			foreach (string name in names)
			{
				if (string.Equals(NormalizeStatusIdentifier(name), b, StringComparison.OrdinalIgnoreCase))
				{
					return true;
				}
			}
			return false;
		}

		private static string NormalizeStatusIdentifier(string value)
		{
			return value.Replace("(Clone)", string.Empty).Trim();
		}

		private static bool IsEquipped(ItemData item)
		{
			object obj = GetMemberValue(item, "m_equipped") ?? GetMemberValue(item, "m_equiped");
			bool flag = default(bool);
			int num;
			if (obj is bool)
			{
				flag = (bool)obj;
				num = 1;
			}
			else
			{
				num = 0;
			}
			return (byte)((uint)num & (flag ? 1u : 0u)) != 0;
		}

		private static string GetObjectName(object? value)
		{
			if (value == null)
			{
				return string.Empty;
			}
			Object val = (Object)((value is Object) ? value : null);
			if (val != null)
			{
				if (!(val == (Object)null))
				{
					return val.name;
				}
				return string.Empty;
			}
			object memberValue = GetMemberValue(value, "m_name");
			if (memberValue != null)
			{
				return memberValue.ToString() ?? string.Empty;
			}
			return value.ToString() ?? string.Empty;
		}

		private static int GetObjectKey(object value)
		{
			Object val = (Object)((value is Object) ? value : null);
			if (val != null && val != (Object)null)
			{
				return val.GetInstanceID();
			}
			return RuntimeHelpers.GetHashCode(value);
		}

		private static string GetMemberText(object? instance, string memberName)
		{
			return GetMemberValue(instance, memberName)?.ToString() ?? "<unavailable>";
		}

		private static object? GetMemberValue(object? instance, string memberName)
		{
			if (instance == null)
			{
				return null;
			}
			try
			{
				FieldInfo fieldInfo = FindField(instance.GetType(), memberName);
				if (fieldInfo != null)
				{
					return fieldInfo.GetValue(instance);
				}
				return FindProperty(instance.GetType(), memberName)?.GetValue(instance, null);
			}
			catch (Exception ex)
			{
				if (Plugin.DebugLogging)
				{
					Plugin.Log.LogDebug((object)("Could not read member '" + memberName + "' from '" + instance.GetType().Name + "': " + ex.GetBaseException().Message));
				}
				return null;
			}
		}

		private static object? GetStaticMemberValue(Type type, string memberName)
		{
			FieldInfo fieldInfo = FindField(type, memberName);
			if (fieldInfo != null && fieldInfo.IsStatic)
			{
				return fieldInfo.GetValue(null);
			}
			PropertyInfo propertyInfo = FindProperty(type, memberName);
			if (!(propertyInfo != null) || !(propertyInfo.GetMethod != null) || !propertyInfo.GetMethod.IsStatic)
			{
				return null;
			}
			return propertyInfo.GetValue(null, null);
		}

		private static bool IsUsableUnityObject(object? value)
		{
			if (value == null)
			{
				return false;
			}
			Object val = (Object)((value is Object) ? value : null);
			if (val != null)
			{
				return val != (Object)null;
			}
			return true;
		}

		private static float TranslateMultiplier(string memberName, float configuredMultiplier, float fallbackNeutral)
		{
			return GetNativeNeutralValue(memberName, fallbackNeutral) + configuredMultiplier - 1f;
		}

		private static float GetNativeNeutralValue(string memberName, float fallback)
		{
			if (NativeNeutralValues.TryGetValue(memberName, out var value))
			{
				return value;
			}
			float num = fallback;
			Type type = typeof(Player).Assembly.GetType("SE_Stats");
			ScriptableObject val = null;
			try
			{
				if (type != null && typeof(ScriptableObject).IsAssignableFrom(type))
				{
					val = ScriptableObject.CreateInstance(type);
					if (TryReadNumber(GetMemberValue(val, memberName), out var number))
					{
						num = number;
					}
				}
			}
			catch (Exception ex)
			{
				if (Plugin.DebugLogging)
				{
					Plugin.Log.LogDebug((object)("Could not read native neutral value for SE_Stats." + memberName + "; " + $"using fallback {fallback:0.###}: {ex.GetBaseException().Message}"));
				}
			}
			finally
			{
				if ((Object)(object)val != (Object)null)
				{
					Object.Destroy((Object)(object)val);
				}
			}
			NativeNeutralValues[memberName] = num;
			if (Plugin.DebugLogging)
			{
				Plugin.Log.LogDebug((object)($"Native neutral SE_Stats.{memberName}={num:0.###}; " + $"configured multiplier 1.0 maps to {num:0.###}."));
			}
			return num;
		}

		private static bool TryReadNumber(object? value, out float number)
		{
			if (value is float num)
			{
				number = num;
				return true;
			}
			if (value is double num2)
			{
				number = (float)num2;
				return true;
			}
			if (value is int num3)
			{
				number = num3;
				return true;
			}
			if (value is long num4)
			{
				number = num4;
				return true;
			}
			number = 0f;
			return false;
		}

		private static void SetText(object? instance, string memberName, string value)
		{
			if (instance == null)
			{
				return;
			}
			try
			{
				FieldInfo fieldInfo = FindField(instance.GetType(), memberName);
				if (fieldInfo != null && fieldInfo.FieldType == typeof(string) && !fieldInfo.IsInitOnly)
				{
					fieldInfo.SetValue(instance, value);
					return;
				}
				PropertyInfo propertyInfo = FindProperty(instance.GetType(), memberName);
				if (propertyInfo != null && propertyInfo.CanWrite && propertyInfo.PropertyType == typeof(string))
				{
					propertyInfo.SetValue(instance, value, null);
				}
			}
			catch (Exception exception)
			{
				LogReflectionWriteFailure(instance, memberName, exception);
			}
		}

		private static void SetObject(object? instance, string memberName, object value)
		{
			if (instance == null)
			{
				return;
			}
			try
			{
				FieldInfo fieldInfo = FindField(instance.GetType(), memberName);
				if (fieldInfo != null && !fieldInfo.IsInitOnly && fieldInfo.FieldType.IsInstanceOfType(value))
				{
					fieldInfo.SetValue(instance, value);
					return;
				}
				PropertyInfo propertyInfo = FindProperty(instance.GetType(), memberName);
				if (propertyInfo != null && propertyInfo.CanWrite && propertyInfo.PropertyType.IsInstanceOfType(value))
				{
					propertyInfo.SetValue(instance, value, null);
				}
			}
			catch (Exception exception)
			{
				LogReflectionWriteFailure(instance, memberName, exception);
			}
		}

		private static void SetFieldValue(object? instance, string memberName, object value)
		{
			if (instance == null)
			{
				return;
			}
			try
			{
				FieldInfo fieldInfo = FindField(instance.GetType(), memberName);
				if (fieldInfo != null && !fieldInfo.IsInitOnly && fieldInfo.FieldType.IsInstanceOfType(value))
				{
					fieldInfo.SetValue(instance, value);
				}
			}
			catch (Exception exception)
			{
				LogReflectionWriteFailure(instance, memberName, exception);
			}
		}

		private static void SetNumber(object? instance, string memberName, float value)
		{
			if (instance == null)
			{
				return;
			}
			try
			{
				FieldInfo fieldInfo = FindField(instance.GetType(), memberName);
				if ((fieldInfo == null || !fieldInfo.IsInitOnly) && fieldInfo != null && TryConvertNumber(value, fieldInfo.FieldType, out object converted))
				{
					fieldInfo.SetValue(instance, converted);
					return;
				}
				PropertyInfo propertyInfo = FindProperty(instance.GetType(), memberName);
				if (propertyInfo != null && propertyInfo.CanWrite && TryConvertNumber(value, propertyInfo.PropertyType, out object converted2))
				{
					propertyInfo.SetValue(instance, converted2, null);
				}
			}
			catch (Exception exception)
			{
				LogReflectionWriteFailure(instance, memberName, exception);
			}
		}

		private static void SetBooleanOrNumber(object? instance, string memberName, bool value)
		{
			if (instance == null)
			{
				return;
			}
			try
			{
				FieldInfo fieldInfo = FindField(instance.GetType(), memberName);
				if (fieldInfo != null)
				{
					object converted;
					if (fieldInfo.FieldType == typeof(bool))
					{
						fieldInfo.SetValue(instance, value);
					}
					else if (TryConvertNumber(value ? 1f : 0f, fieldInfo.FieldType, out converted))
					{
						fieldInfo.SetValue(instance, converted);
					}
				}
			}
			catch (Exception exception)
			{
				LogReflectionWriteFailure(instance, memberName, exception);
			}
		}

		private static void SetEnumOrString(object? instance, string memberName, string value)
		{
			if (instance == null)
			{
				return;
			}
			try
			{
				FieldInfo fieldInfo = FindField(instance.GetType(), memberName);
				if (fieldInfo == null)
				{
					return;
				}
				if (fieldInfo.FieldType == typeof(string))
				{
					fieldInfo.SetValue(instance, value);
				}
				else if (fieldInfo.FieldType.IsEnum)
				{
					try
					{
						fieldInfo.SetValue(instance, Enum.Parse(fieldInfo.FieldType, value, ignoreCase: true));
						return;
					}
					catch (ArgumentException)
					{
						return;
					}
				}
			}
			catch (Exception exception)
			{
				LogReflectionWriteFailure(instance, memberName, exception);
			}
		}

		private static void LogReflectionWriteFailure(object instance, string memberName, Exception exception)
		{
			if (Plugin.DebugLogging)
			{
				Plugin.Log.LogDebug((object)("Could not write member '" + memberName + "' on '" + instance.GetType().Name + "': " + exception.GetBaseException().Message));
			}
		}

		private static bool TryConvertNumber(float value, Type targetType, out object? converted)
		{
			if (targetType == typeof(float))
			{
				converted = value;
				return true;
			}
			if (targetType == typeof(double))
			{
				converted = (double)value;
				return true;
			}
			if (targetType == typeof(int))
			{
				converted = (int)value;
				return true;
			}
			if (targetType == typeof(bool))
			{
				converted = Math.Abs(value) > 0.001f;
				return true;
			}
			converted = null;
			return false;
		}

		private static object? InvokeNoArgumentMethod(object target, string methodName)
		{
			MethodInfo[] methods = target.GetType().GetMethods(AnyInstance);
			foreach (MethodInfo methodInfo in methods)
			{
				if (string.Equals(methodInfo.Name, methodName, StringComparison.Ordinal) && methodInfo.GetParameters().Length == 0)
				{
					try
					{
						return methodInfo.Invoke(target, null);
					}
					catch
					{
						return null;
					}
				}
			}
			return null;
		}

		private static float? InvokeNumberGetter(object target, string methodName)
		{
			object obj = InvokeNoArgumentMethod(target, methodName);
			if (obj is float)
			{
				return (float)obj;
			}
			if (obj is double num)
			{
				return (float)num;
			}
			if (obj is int num2)
			{
				return num2;
			}
			return null;
		}

		private static PlayerState GetState(Player player)
		{
			int instanceID = ((Object)player).GetInstanceID();
			if (!States.TryGetValue(instanceID, out PlayerState value))
			{
				value = new PlayerState();
				States[instanceID] = value;
			}
			return value;
		}

		private static FieldInfo? FindField(Type type, string name)
		{
			Type type2 = type;
			while (type2 != null)
			{
				FieldInfo field = type2.GetField(name, AnyMember);
				if (field != null)
				{
					return field;
				}
				type2 = type2.BaseType;
			}
			return null;
		}

		private static PropertyInfo? FindProperty(Type type, string name)
		{
			Type type2 = type;
			while (type2 != null)
			{
				PropertyInfo property = type2.GetProperty(name, AnyMember);
				if (property != null)
				{
					return property;
				}
				type2 = type2.BaseType;
			}
			return null;
		}
	}
	[HarmonyPatch]
	internal static class PlayerUpdatePatch
	{
		private static MethodBase? TargetMethod()
		{
			return AccessTools.Method(typeof(Player), "Update", (Type[])null, (Type[])null) ?? AccessTools.Method(typeof(Character), "Update", (Type[])null, (Type[])null);
		}

		private static bool Prepare()
		{
			return TargetMethod() != null;
		}

		[HarmonyPostfix]
		private static void Postfix(object __instance)
		{
			Player val = (Player)((__instance is Player) ? __instance : null);
			if (val != null)
			{
				TrinketEffectManager.Tick(val);
			}
		}
	}
	[HarmonyPatch]
	internal static class EquipmentStatusEffectsPatch
	{
		private static MethodBase? TargetMethod()
		{
			return AccessTools.Method(typeof(Player), "UpdateEquipmentStatusEffects", (Type[])null, (Type[])null) ?? AccessTools.Method(typeof(Character), "UpdateEquipmentStatusEffects", (Type[])null, (Type[])null);
		}

		private static bool Prepare()
		{
			return TargetMethod() != null;
		}

		[HarmonyPostfix]
		private static void Postfix(object __instance)
		{
			Player val = (Player)((__instance is Player) ? __instance : null);
			if (val != null)
			{
				TrinketEffectManager.RefreshAfterEquipmentStatusEffects(val);
			}
		}
	}
	[HarmonyPatch]
	internal static class PlayerSaveDataPatch
	{
		private static readonly BindingFlags AnyInstance = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic;

		private static readonly BindingFlags AnyStatic = BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic;

		internal static bool IsAvailable => HasTargetMethods();

		private static IEnumerable<MethodBase> TargetMethods()
		{
			List<MethodBase> found = new List<MethodBase>();
			MethodInfo[] methods = typeof(Player).GetMethods(AnyInstance);
			foreach (MethodInfo methodInfo in methods)
			{
				if (IsPlayerSaveMethod(methodInfo) && !found.Contains(methodInfo))
				{
					found.Add(methodInfo);
					yield return methodInfo;
				}
			}
			methods = typeof(ZNet).GetMethods(AnyInstance);
			foreach (MethodInfo methodInfo2 in methods)
			{
				if (string.Equals(methodInfo2.Name, "Save", StringComparison.Ordinal) && !found.Contains(methodInfo2))
				{
					found.Add(methodInfo2);
					yield return methodInfo2;
				}
			}
		}

		private static bool HasTargetMethods()
		{
			using (IEnumerator<MethodBase> enumerator = TargetMethods().GetEnumerator())
			{
				if (enumerator.MoveNext())
				{
					_ = enumerator.Current;
					return true;
				}
			}
			return false;
		}

		private static bool Prepare()
		{
			return HasTargetMethods();
		}

		private static bool IsPlayerSaveMethod(MethodInfo method)
		{
			if (!string.Equals(method.Name, "GetSaveData", StringComparison.Ordinal) && !string.Equals(method.Name, "SavePlayerData", StringComparison.Ordinal) && !string.Equals(method.Name, "SerializePlayerData", StringComparison.Ordinal))
			{
				return string.Equals(method.Name, "Save", StringComparison.Ordinal);
			}
			return true;
		}

		private static Player? GetCurrentPlayer(object instance)
		{
			Player val = (Player)((instance is Player) ? instance : null);
			if (val != null)
			{
				return val;
			}
			object? obj = typeof(Player).GetField("m_localPlayer", AnyStatic)?.GetValue(null);
			Player val2 = (Player)((obj is Player) ? obj : null);
			if (val2 != null)
			{
				return val2;
			}
			object? obj2 = typeof(Player).GetProperty("localPlayer", AnyStatic)?.GetValue(null, null);
			return (Player?)((obj2 is Player) ? obj2 : null);
		}

		[HarmonyPrefix]
		private static void Prefix(object __instance)
		{
			Player currentPlayer = GetCurrentPlayer(__instance);
			if ((Object)(object)currentPlayer != (Object)null)
			{
				TrinketEffectManager.BeforePlayerSave(currentPlayer);
			}
		}

		[HarmonyPostfix]
		private static void Postfix(object __instance)
		{
			Player currentPlayer = GetCurrentPlayer(__instance);
			if ((Object)(object)currentPlayer != (Object)null)
			{
				TrinketEffectManager.AfterPlayerSave(currentPlayer);
			}
		}

		[HarmonyFinalizer]
		private static Exception? Finalizer(object __instance, Exception? __exception)
		{
			Player currentPlayer = GetCurrentPlayer(__instance);
			if ((Object)(object)currentPlayer != (Object)null)
			{
				TrinketEffectManager.AfterPlayerSave(currentPlayer);
			}
			return __exception;
		}
	}
	[HarmonyPatch]
	internal static class TrinketTooltipPatch
	{
		private const BindingFlags AnyInstance = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic;

		private const BindingFlags AnyStatic = BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic;

		internal static bool IsAvailable => HasTargetMethods();

		private static IEnumerable<MethodBase> TargetMethods()
		{
			MethodInfo[] methods = typeof(ItemData).GetMethods(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
			foreach (MethodInfo methodInfo in methods)
			{
				if (string.Equals(methodInfo.Name, "GetTooltip", StringComparison.Ordinal) && methodInfo.ReturnType == typeof(string))
				{
					yield return methodInfo;
				}
			}
		}

		private static bool HasTargetMethods()
		{
			using (IEnumerator<MethodBase> enumerator = TargetMethods().GetEnumerator())
			{
				if (enumerator.MoveNext())
				{
					_ = enumerator.Current;
					return true;
				}
			}
			return false;
		}

		private static bool Prepare()
		{
			return HasTargetMethods();
		}

		[HarmonyPostfix]
		private static void Postfix(ItemData __instance, ref string __result)
		{
			if (__instance == null || (Object)(object)__instance.m_dropPrefab == (Object)null)
			{
				return;
			}
			string itemName = ((__instance.m_shared == null) ? string.Empty : __instance.m_shared.m_name);
			TrinketDefinition trinketDefinition = TrinketConfigRegistry.FindByPrefab(((Object)__instance.m_dropPrefab).name, itemName);
			if (trinketDefinition == null)
			{
				return;
			}
			try
			{
				string configuredDescription = BuildConfiguredDescription(trinketDefinition);
				__result = ReplaceVanillaDescription(__instance, __result ?? string.Empty, configuredDescription);
			}
			catch (Exception ex)
			{
				Plugin.Log.LogWarning((object)("Could not update tooltip for '" + trinketDefinition.PrefabName + "': " + ex.GetBaseException().Message));
			}
		}

		private static string BuildConfiguredDescription(TrinketDefinition definition)
		{
			StringBuilder stringBuilder = new StringBuilder();
			StringBuilder stringBuilder2 = new StringBuilder();
			StringBuilder stringBuilder3 = new StringBuilder();
			AppendEffects(stringBuilder2, definition, doubled: false);
			if (Plugin.DoublingOnAdrenaline.Value)
			{
				AppendEffects(stringBuilder3, definition, doubled: true);
			}
			stringBuilder.AppendLine("<color=#D9B56D>Configured passive effects:</color>");
			stringBuilder.Append((stringBuilder2.Length == 0) ? "None\n" : stringBuilder2.ToString());
			if (stringBuilder3.Length > 0)
			{
				stringBuilder.AppendLine();
				stringBuilder.AppendLine("<color=#D9B56D>When adrenaline is doubled:</color>");
				stringBuilder.Append((object?)stringBuilder3);
			}
			return stringBuilder.ToString().TrimEnd('\r', '\n');
		}

		private static void AppendEffects(StringBuilder output, TrinketDefinition definition, bool doubled)
		{
			float value = (doubled ? definition.DoubledHealthRegen.Value : definition.PassiveHealthRegen.Value);
			float value2 = (doubled ? definition.DoubledStaminaRegen.Value : definition.PassiveStaminaRegen.Value);
			float value3 = (doubled ? definition.DoubledEitrRegen.Value : definition.PassiveEitrRegen.Value);
			float value4 = (doubled ? definition.DoubledArmor.Value : definition.PassiveArmor.Value);
			float value5 = (doubled ? definition.DoubledSpeed.Value : definition.PassiveSpeed.Value);
			float value6 = (doubled ? definition.DoubledSwimSpeed.Value : definition.PassiveSwimSpeed.Value);
			float value7 = (doubled ? definition.DoubledJump.Value : definition.PassiveJump.Value);
			float value8 = (doubled ? definition.DoubledFallDamage.Value : definition.PassiveFallDamage.Value);
			float value9 = (doubled ? definition.DoubledNoise.Value : definition.PassiveNoise.Value);
			float value10 = (doubled ? definition.DoubledStealth.Value : definition.PassiveStealth.Value);
			float value11 = (doubled ? definition.DoubledMaxCarryWeight.Value : definition.PassiveMaxCarryWeight.Value);
			float value12 = (doubled ? definition.DoubledTimedBlockBonus.Value : definition.PassiveTimedBlockBonus.Value);
			float value13 = (doubled ? definition.DoubledStagger.Value : definition.PassiveStagger.Value);
			float value14 = (doubled ? definition.DoubledDamageModifier.Value : definition.PassiveDamageModifier.Value);
			string text = (doubled ? definition.DoubledResistance.Value : definition.PassiveResistance.Value);
			AddMultiplier(output, "health regeneration", value);
			AddMultiplier(output, "stamina regeneration", value2);
			AddMultiplier(output, "eitr regeneration", value3);
			AddFlat(output, "armor", value4);
			AddPercent(output, "movement speed", value5);
			AddPercent(output, "swim speed", value6);
			AddValue(output, "jump modifier", value7);
			AddMultiplier(output, "fall damage", value8);
			AddMultiplier(output, "noise", value9);
			AddMultiplier(output, "stealth effectiveness", value10);
			AddMultiplier(output, "stagger", value13);
			AddFlat(output, "maximum carry weight", value11);
			AddFlat(output, "timed-block bonus", value12);
			AddPercent(output, "general damage", value14);
			AddStaminaMultiplier(output, "run", doubled ? definition.DoubledRunStaminaUse.Value : definition.PassiveRunStaminaUse.Value);
			AddStaminaMultiplier(output, "jump", doubled ? definition.DoubledJumpStaminaUse.Value : definition.PassiveJumpStaminaUse.Value);
			AddStaminaMultiplier(output, "attack", doubled ? definition.DoubledAttackStaminaUse.Value : definition.PassiveAttackStaminaUse.Value);
			AddStaminaMultiplier(output, "block", doubled ? definition.DoubledBlockStaminaUse.Value : definition.PassiveBlockStaminaUse.Value);
			AddStaminaMultiplier(output, "dodge", doubled ? definition.DoubledDodgeStaminaUse.Value : definition.PassiveDodgeStaminaUse.Value);
			AddStaminaMultiplier(output, "swim", doubled ? definition.DoubledSwimStaminaUse.Value : definition.PassiveSwimStaminaUse.Value);
			AddStaminaMultiplier(output, "home-item", doubled ? definition.DoubledHomeItemStaminaUse.Value : definition.PassiveHomeItemStaminaUse.Value);
			AddStaminaMultiplier(output, "sneak", doubled ? definition.DoubledSneakStaminaUse.Value : definition.PassiveSneakStaminaUse.Value);
			if (!string.IsNullOrWhiteSpace(text) && !string.Equals(text, "Normal", StringComparison.OrdinalIgnoreCase))
			{
				output.AppendLine("Damage resistance: " + text + ".");
			}
			AddSkill(output, definition.GetSkillName(1, doubled), definition.GetSkillBonus(1, doubled));
			AddSkill(output, definition.GetSkillName(2, doubled), definition.GetSkillBonus(2, doubled));
			string raiseSkillName = definition.GetRaiseSkillName(doubled);
			if (!string.Equals(raiseSkillName, "None", StringComparison.OrdinalIgnoreCase))
			{
				AddMultiplier(output, raiseSkillName + " skill gain", definition.GetRaiseSkillBonus(doubled));
			}
			string[] damageTypes = TrinketDefinition.DamageTypes;
			foreach (string text2 in damageTypes)
			{
				AddPercent(output, text2 + " damage", definition.GetDamage(text2, doubled));
			}
			if (doubled && definition.Duration.Value > 0.0001f)
			{
				output.AppendLine("Duration: " + Format(definition.Duration.Value) + " seconds.");
			}
		}

		private static void AddSkill(StringBuilder output, string skill, float value)
		{
			if (!string.Equals(skill, "None", StringComparison.OrdinalIgnoreCase) && Math.Abs(value) > 0.0001f)
			{
				output.AppendLine(((value >= 0f) ? "+" : string.Empty) + Format(value) + " " + skill + " skill.");
			}
		}

		private static void AddFlat(StringBuilder output, string label, float value)
		{
			if (Math.Abs(value) > 0.0001f)
			{
				output.AppendLine(((value >= 0f) ? "+" : string.Empty) + Format(value) + " " + label + ".");
			}
		}

		private static void AddValue(StringBuilder output, string label, float value)
		{
			if (Math.Abs(value) > 0.0001f)
			{
				output.AppendLine(label + ": " + Format(value) + ".");
			}
		}

		private static void AddPercent(StringBuilder output, string label, float value)
		{
			if (Math.Abs(value) > 0.0001f)
			{
				output.AppendLine(((value >= 0f) ? "+" : string.Empty) + Format(value * 100f) + "% " + label + ".");
			}
		}

		private static void AddMultiplier(StringBuilder output, string label, float value)
		{
			if (Math.Abs(value - 1f) > 0.0001f)
			{
				float num = (value - 1f) * 100f;
				output.AppendLine(((num >= 0f) ? "+" : string.Empty) + Format(num) + "% " + label + ".");
			}
		}

		private static void AddStaminaMultiplier(StringBuilder output, string action, float value)
		{
			if (Math.Abs(value - 1f) > 0.0001f)
			{
				float num = (value - 1f) * 100f;
				output.AppendLine(((num >= 0f) ? "+" : string.Empty) + Format(num) + "% " + action + " stamina cost.");
			}
		}

		private static string Format(float value)
		{
			return value.ToString("0.##", CultureInfo.InvariantCulture);
		}

		private static string ReplaceVanillaDescription(ItemData item, string tooltip, string configuredDescription)
		{
			string rawDescription = GetRawDescription(item);
			if (string.IsNullOrWhiteSpace(rawDescription))
			{
				return tooltip + "\n\n" + configuredDescription;
			}
			string text = Localize(rawDescription);
			if (!string.IsNullOrEmpty(text) && tooltip.IndexOf(text, StringComparison.Ordinal) >= 0)
			{
				return tooltip.Replace(text, configuredDescription);
			}
			if (tooltip.IndexOf(rawDescription, StringComparison.Ordinal) >= 0)
			{
				return tooltip.Replace(rawDescription, configuredDescription);
			}
			return tooltip + "\n\n" + configuredDescription;
		}

		private static string GetRawDescription(ItemData item)
		{
			object shared = item.m_shared;
			if (shared == null)
			{
				return string.Empty;
			}
			return (shared.GetType().GetField("m_description", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)?.GetValue(shared) as string) ?? string.Empty;
		}

		private static string Localize(string text)
		{
			Type type = typeof(Player).Assembly.GetType("Localization");
			if (type == null)
			{
				return text;
			}
			object obj = type.GetField("instance", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic)?.GetValue(null);
			if (obj == null)
			{
				return text;
			}
			MethodInfo[] methods = type.GetMethods(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
			foreach (MethodInfo methodInfo in methods)
			{
				if (!string.Equals(methodInfo.Name, "Localize", StringComparison.Ordinal) || methodInfo.ReturnType != typeof(string))
				{
					continue;
				}
				ParameterInfo[] parameters = methodInfo.GetParameters();
				if (parameters.Length == 1 && !(parameters[0].ParameterType != typeof(string)))
				{
					try
					{
						return (string)methodInfo.Invoke(obj, new object[1] { text });
					}
					catch
					{
						return text;
					}
				}
			}
			return text;
		}
	}
}