Decompiled source of EpicLoot ProgressionFix v1.24.1

plugins/EpicLootProgressionFix.dll

Decompiled 16 hours ago
using System;
using System.Collections;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Net.Sockets;
using System.Net.WebSockets;
using System.Numerics;
using System.Reflection;
using System.Reflection.Emit;
using System.Runtime.CompilerServices;
using System.Security.Authentication;
using System.Security.Cryptography;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading;
using System.Threading.Tasks;
using BepInEx;
using BepInEx.Bootstrap;
using BepInEx.Configuration;
using BepInEx.Logging;
using Common;
using EpicLoot;
using EpicLoot.Adventure;
using EpicLoot.Adventure.Feature;
using EpicLoot.Biomes;
using EpicLoot.Compendium;
using EpicLoot.Crafting;
using EpicLoot.CraftingV2;
using EpicLoot.GatedItemType;
using EpicLoot.LegendarySystem;
using EpicLoot.LootBeams;
using EpicLoot.Magic.MagicItemEffects;
using EpicLoot.MagicItemEffects;
using EpicLoot.MagicItemEffects.Shards;
using EpicLoot.ShardStones;
using EpicLoot.Transformation;
using EpicLoot.src.Magic.MagicItemEffects.Helpers;
using EpicLoot_UnityLib;
using HarmonyLib;
using HarmonyLib.Public.Patching;
using Jotunn.Configs;
using Jotunn.Entities;
using Jotunn.Managers;
using Jotunn.Utils;
using Microsoft.CodeAnalysis;
using MonoMod.Cil;
using MonoMod.Utils;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using Newtonsoft.Json.Serialization;
using SoftReferenceableAssets;
using Splatform;
using TMPro;
using UnityEngine;
using UnityEngine.AI;
using UnityEngine.Audio;
using UnityEngine.EventSystems;
using UnityEngine.Events;
using UnityEngine.Networking;
using UnityEngine.Rendering;
using UnityEngine.SceneManagement;
using UnityEngine.UI;
using Valheim.SettingsGui;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: AssemblyVersion("0.0.0.0")]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)]
	internal sealed class RefSafetyRulesAttribute : Attribute
	{
		public readonly int Version;

		public RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
}
namespace EpicLootProgressionFix
{
	internal static class AchievementAnnouncements
	{
		private static readonly HashSet<string> ObservedUnlocks = new HashSet<string>();

		private static FieldInfo discordInstance;

		private static object chat;

		private static object defaultRoute;

		private static ManualLogSource logger;

		[ThreadStatic]
		private static int suppressedUnlocks;

		internal static void Initialize(IntegrationPatches harmony, ManualLogSource log)
		{
			//IL_0089: Unknown result type (might be due to invalid IL or missing references)
			//IL_009e: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bf: Expected O, but got Unknown
			//IL_00bf: Expected O, but got Unknown
			//IL_00bf: Expected O, but got Unknown
			if (DiscordBot.Installed)
			{
				logger = log;
				discordInstance = AccessTools.Field(DiscordBot.Discord, "instance");
				if (discordInstance == null)
				{
					throw new InvalidOperationException("DiscordBot achievement relay API changed.");
				}
				chat = Enum.Parse(DiscordBot.Webhook, "Chat");
				defaultRoute = Enum.Parse(DiscordBot.WebhookRoute, "Default");
				harmony.Patch(AccessTools.Method(typeof(Achievements), "AchievementEvent", (Type[])null, (Type[])null), new HarmonyMethod(typeof(AchievementAnnouncements), "BeforeUnlock", (Type[])null), new HarmonyMethod(typeof(AchievementAnnouncements), "AfterUnlock", (Type[])null), null, new HarmonyMethod(typeof(AchievementAnnouncements), "AfterUnlockTree", (Type[])null));
			}
		}

		private static void BeforeUnlock(bool synchronize, out int __state)
		{
			__state = suppressedUnlocks;
			if (!synchronize)
			{
				suppressedUnlocks++;
			}
		}

		private static void AfterUnlockTree(int __state)
		{
			suppressedUnlocks = __state;
		}

		private static void AfterUnlock(Achievement ach)
		{
			if ((Object)(object)ach == (Object)null || !ObservedUnlocks.Add(ach.m_id) || suppressedUnlocks != 0 || !Plugin.DiscordAchievements.Value || !Plugin.Ready)
			{
				return;
			}
			Player localPlayer = Player.m_localPlayer;
			ZRpc val = (((Object)(object)ZNet.instance != (Object)null) ? ZNet.instance.GetServerRPC() : null);
			if ((Object)(object)localPlayer == (Object)null || val == null || !val.IsConnected())
			{
				return;
			}
			try
			{
				object value = discordInstance.GetValue(null);
				if (value == null)
				{
					throw new InvalidOperationException("DiscordBot is not ready to relay the achievement.");
				}
				string text = English(ach.m_name);
				string text2 = English(ach.m_description);
				if (string.IsNullOrWhiteSpace(text) || string.IsNullOrWhiteSpace(text2))
				{
					throw new InvalidOperationException("Achievement " + ach.m_id + " has no English name or requirement description.");
				}
				string text3 = PlainText(localPlayer.GetPlayerName());
				string text4 = text3 + " earned the achievement \"" + text + "\" [" + text2 + "]";
				DiscordBot.SendMessage.Invoke(value, new object[5]
				{
					chat,
					text3,
					DiscordSystemMessages.Box(text4),
					null,
					defaultRoute
				});
			}
			catch (Exception ex)
			{
				logger.LogError((object)("Could not announce achievement " + ach.m_id + ": " + ex.GetBaseException().Message));
			}
		}

		private static string English(string text)
		{
			if (!EnglishText.TryTranslate(text, out var english))
			{
				throw new InvalidOperationException("Missing English achievement text: " + english);
			}
			return PlainText(english);
		}

		private static string PlainText(string text)
		{
			text = DiscordSystemMessages.StripTags(text).Replace("\\n", " ");
			return Regex.Replace(text, "\\s+", " ").Trim();
		}
	}
	[HarmonyPatch]
	internal static class ActiveEffectsPage
	{
		private enum Section
		{
			Resources,
			Defense,
			Movement,
			Attacks,
			Skills,
			Utility,
			Other
		}

		private sealed class Page : MagicTextInfo
		{
			internal Page()
				: base(Localization.instance.Localize("$inventory_activeeffects"), true)
			{
			}

			public override void Build(MagicPages pages)
			{
				shownOn = pages;
				shownRows = null;
				pages.TitleElement.SetFontSize(30);
				pages.TitleElement.SetSize(pages.MinWidth, 50f);
				if (!((Object)(object)Player.m_localPlayer == (Object)null))
				{
					List<(string, string)> tooltips = new List<(string, string)>();
					Fill(pages, RowsWithTooltips(Player.m_localPlayer, tooltips), tooltips);
				}
			}
		}

		private const float RefreshSeconds = 0.5f;

		private const string ValueColor = "#ffa500";

		private const string MutedColor = "#9a9a9a";

		private const string HeadingColor = "#ffa626";

		private const int TitleFontSize = 30;

		private const int HeadingFontSize = 20;

		private const int RowFontSize = 16;

		private const float TitleHeight = 50f;

		private const float HeadingHeight = 34f;

		private const float RowSpacing = 2f;

		private const float ColumnSpacing = 24f;

		private static readonly CultureInfo Numbers;

		private static readonly FieldRef<TextsDialog, List<TextInfo>> Texts;

		private static readonly FieldRef<Skills, Dictionary<SkillType, Skill>> SkillData;

		private static readonly FieldRef<Humanoid, float> BlockTimer;

		private static readonly FieldRef<SE_Demister, GameObject> WispBall;

		private static readonly Func<Player, float> DodgeStamina;

		private static readonly Func<Character, float> JogFactor;

		private static readonly Func<Character, float> RunFactor;

		private static readonly Action<MagicTextElement, string> SetText;

		private static readonly FieldRef<MagicTextList, VerticalLayoutGroup> ListLayout;

		private static readonly FieldRef<MagicTextList, ScrollRect> ListScroll;

		private static readonly FieldRef<MagicTextElement, GameObject> ElementObject;

		private static readonly FieldRef<UITooltip> ShownTooltip;

		private static readonly (DamageType Type, string Name)[] DamageTypes;

		private static readonly string[] SectionNames;

		private static readonly HashSet<string> Included;

		private static readonly Dictionary<string, Section> Sections;

		private static readonly string[] CriticalHealth;

		private static readonly string[] Daytime;

		private static readonly string[] Nighttime;

		private static readonly string[] WhileRested;

		private static readonly Dictionary<string, string> EpicLootStatusTexts;

		private static Page page;

		private static MagicPages shownOn;

		private static List<string> shownRows;

		private static readonly List<UITooltip> statusTooltips;

		private static GameObject columns;

		private static float nextRefresh;

		static ActiveEffectsPage()
		{
			Numbers = CultureInfo.InvariantCulture;
			Texts = AccessTools.FieldRefAccess<TextsDialog, List<TextInfo>>("m_texts");
			SkillData = AccessTools.FieldRefAccess<Skills, Dictionary<SkillType, Skill>>("m_skillData");
			BlockTimer = AccessTools.FieldRefAccess<Humanoid, float>("m_blockTimer");
			WispBall = AccessTools.FieldRefAccess<SE_Demister, GameObject>("m_ballInstance");
			DodgeStamina = AccessTools.MethodDelegate<Func<Player, float>>(AccessTools.Method(typeof(Player), "GetDodgeStaminaUse", (Type[])null, (Type[])null), (object)null, true);
			JogFactor = AccessTools.MethodDelegate<Func<Character, float>>(AccessTools.Method(typeof(Character), "GetJogSpeedFactor", (Type[])null, (Type[])null), (object)null, true);
			RunFactor = AccessTools.MethodDelegate<Func<Character, float>>(AccessTools.Method(typeof(Character), "GetRunSpeedFactor", (Type[])null, (Type[])null), (object)null, true);
			SetText = AccessTools.MethodDelegate<Action<MagicTextElement, string>>(AccessTools.Method(typeof(MagicTextElement), "Set", (Type[])null, (Type[])null), (object)null, true);
			ListLayout = AccessTools.FieldRefAccess<MagicTextList, VerticalLayoutGroup>("_layout");
			ListScroll = AccessTools.FieldRefAccess<MagicTextList, ScrollRect>("_scrollRect");
			ElementObject = AccessTools.FieldRefAccess<MagicTextElement, GameObject>("_obj");
			ShownTooltip = AccessTools.StaticFieldRefAccess<UITooltip>(AccessTools.Field(typeof(UITooltip), "m_current"));
			DamageTypes = new(DamageType, string)[8]
			{
				((DamageType)1, "Blunt"),
				((DamageType)2, "Slash"),
				((DamageType)4, "Pierce"),
				((DamageType)32, "Fire"),
				((DamageType)64, "Frost"),
				((DamageType)128, "Lightning"),
				((DamageType)256, "Poison"),
				((DamageType)512, "Spirit")
			};
			SectionNames = new string[7] { "Health and resources", "Defense", "Movement", "Attacks", "Skills", "Utility", "Other effects" };
			Included = new HashSet<string>
			{
				MagicEffectType.AddAllSkills,
				MagicEffectType.Interconnected,
				MagicEffectType.ConvertHealthToEitr,
				MagicEffectType.DeepWell,
				MagicEffectType.SlightlyWeakToFire,
				MagicEffectType.WeakToFire,
				MagicEffectType.VeryWeakToFire,
				MagicEffectType.IncreaseHealth,
				MagicEffectType.IncreaseStamina,
				MagicEffectType.IncreaseEitr,
				MagicEffectType.PercentHealth,
				MagicEffectType.PercentStamina,
				MagicEffectType.PercentEitr,
				MagicEffectType.BulkUp,
				MagicEffectType.EnergeticEitr,
				MagicEffectType.HeartyEitr,
				MagicEffectType.EveryXPointsOfEitrIncreasesStamina,
				MagicEffectType.GainMaxStaminaBasedOnPlayerMaxHealth,
				MagicEffectType.StaminaIncreaseForMovementPenalty,
				MagicEffectType.DartingThoughts,
				MagicEffectType.ModifyHealthRegen,
				MagicEffectType.AddHealthRegen,
				MagicEffectType.ModifyStaminaRegen,
				MagicEffectType.ModifyEitrRegen,
				MagicEffectType.StaminaRegenBonusFromPlayerWeight,
				MagicEffectType.BloodMagicLevelIncreasesHealthRegen,
				MagicEffectType.ModifyLowHealth,
				MagicEffectType.ModifyArmor,
				MagicEffectType.ArmorFromMovementPenalty,
				MagicEffectType.ReduceArmorIncreaseDamage,
				MagicEffectType.AddFireResistancePercentage,
				MagicEffectType.AddFrostResistancePercentage,
				MagicEffectType.AddLightningResistancePercentage,
				MagicEffectType.AddPoisonResistancePercentage,
				MagicEffectType.AddSpiritResistancePercentage,
				MagicEffectType.AddElementalResistancePercentage,
				MagicEffectType.AddBluntResistancePercentage,
				MagicEffectType.AddSlashingResistancePercentage,
				MagicEffectType.AddPiercingResistancePercentage,
				MagicEffectType.AddChoppingResistancePercentage,
				MagicEffectType.AddPhysicalResistancePercentage,
				MagicEffectType.PhysToFire,
				MagicEffectType.PhysToFrost,
				MagicEffectType.PhysToPoison,
				MagicEffectType.PhysToLightning,
				MagicEffectType.ModifyBlockPower,
				MagicEffectType.ModifyParry,
				MagicEffectType.ModifyBlockForce,
				MagicEffectType.ModifyParryWindow,
				MagicEffectType.BurdenedBlock,
				MagicEffectType.AnchoredBlock,
				MagicEffectType.Duelist,
				MagicEffectType.ModifyBlockStaminaUse,
				MagicEffectType.ModifyMovementSpeed,
				MagicEffectType.RemoveSpeedPenalty,
				MagicEffectType.TravelLight,
				MagicEffectType.ModifySprintStaminaUse,
				MagicEffectType.ModifyJumpStaminaUse,
				MagicEffectType.ModifyDodgeStaminaUse,
				MagicEffectType.DecreaseDodgeCost,
				MagicEffectType.ModifyNoise,
				MagicEffectType.AddCarryWeight,
				MagicEffectType.Megingjord,
				MagicEffectType.CarryWeightForMovementPenalty,
				MagicEffectType.ReduceWeight,
				MagicEffectType.Weightless,
				MagicEffectType.IncreaseHeatResistance,
				MagicEffectType.ModifyDamage,
				MagicEffectType.ModifyPhysicalDamage,
				MagicEffectType.ModifyElementalDamage,
				MagicEffectType.AddBluntDamage,
				MagicEffectType.AddSlashingDamage,
				MagicEffectType.AddPiercingDamage,
				MagicEffectType.AddFireDamage,
				MagicEffectType.AddFrostDamage,
				MagicEffectType.AddLightningDamage,
				MagicEffectType.AddPoisonDamage,
				MagicEffectType.AddSpiritDamage,
				MagicEffectType.CoinHoarder,
				MagicEffectType.ConvertPhysicalDamageToLightning,
				MagicEffectType.IcyWeight,
				MagicEffectType.DamageBonusFromPlayerWeight,
				MagicEffectType.DamageIncreaseFromMovementPenalty,
				MagicEffectType.NecroticFire,
				MagicEffectType.AddSwordsSkill,
				MagicEffectType.AddKnivesSkill,
				MagicEffectType.AddClubsSkill,
				MagicEffectType.AddPolearmsSkill,
				MagicEffectType.AddSpearsSkill,
				MagicEffectType.AddBlockingSkill,
				MagicEffectType.AddAxesSkill,
				MagicEffectType.AddBowsSkill,
				MagicEffectType.AddCrossbowsSkill,
				MagicEffectType.AddUnarmedSkill,
				MagicEffectType.AddPickaxesSkill,
				MagicEffectType.AddFishingSkill,
				MagicEffectType.AddElementalMagicSkill,
				MagicEffectType.AddBloodMagicSkill,
				MagicEffectType.AddMovementSkills,
				MagicEffectType.AddCrafterSkills,
				MagicEffectType.IncreaseMeleeSkills,
				MagicEffectType.IncreaseRangedSkills,
				MagicEffectType.BlockAsDodgeAsBlock,
				MagicEffectType.BlockAsWoodCuttingAndPickaxes,
				MagicEffectType.Comfortable,
				MagicEffectType.FeatherFall,
				MagicEffectType.ModifyDurability,
				MagicEffectType.Indestructible,
				MagicEffectType.SailingSpeed,
				MagicEffectType.DaySailingSpeed
			};
			Sections = new Dictionary<string, Section>();
			CriticalHealth = new string[11]
			{
				MagicEffectType.ModifyArmorLowHealth,
				MagicEffectType.ModifyDamageLowHealth,
				MagicEffectType.ModifyBlockPowerLowHealth,
				MagicEffectType.ModifyParryLowHealth,
				MagicEffectType.ModifyMovementSpeedLowHealth,
				MagicEffectType.ModifyHealthRegenLowHealth,
				MagicEffectType.ModifyStaminaRegenLowHealth,
				MagicEffectType.ModifyEitrRegenLowHealth,
				MagicEffectType.ModifyAttackSpeedLowHealth,
				MagicEffectType.AvoidDamageTakenLowHealth,
				MagicEffectType.LifeStealLowHealth
			};
			Daytime = new string[6]
			{
				MagicEffectType.DayArmor,
				MagicEffectType.DayHealthRegen,
				MagicEffectType.DayStaminaRegen,
				MagicEffectType.IncreaseDamageDuringDaytime,
				MagicEffectType.DayDiscovery,
				MagicEffectType.DayBlocker
			};
			Nighttime = new string[5]
			{
				MagicEffectType.DamageReductionAtNight,
				MagicEffectType.NightStaminaRegenIncrease,
				MagicEffectType.IncreaseDamageDuringNighttime,
				MagicEffectType.NightCarryWeight,
				MagicEffectType.NightBlocker
			};
			WhileRested = new string[2]
			{
				MagicEffectType.RestingHealthRegen,
				MagicEffectType.GainMaxCarryWeightFromRested
			};
			EpicLootStatusTexts = new Dictionary<string, string>
			{
				["EL_AssassinCooldown"] = "Your Assassin strike is recharging.",
				["EL_BonemassPoisonCooldown"] = "Enemies you kill won't burst in venom until this ends.",
				["EL_EitrBarrier"] = "Part of every hit is taken from your eitr first.",
				["EL_EitrInfusion"] = "Your hits deal extra damage. Your eitr doesn't regenerate.",
				["EL_ForestsAidCooldown"] = "Roots won't erupt when you are hit until this ends.",
				["EL_Frostwalker"] = "Water freezes under your feet, draining your eitr. Crouch to break through.",
				["EL_IcyRetributionCooldown"] = "You won't release a frost nova when hit until this ends.",
				["EL_MeteorSummonerCharge"] = "Your weapon hits build charges. At full charge, your next hit calls down a meteor.",
				["EL_RunningOnEmptyCooldown"] = "You won't burn health for stamina until this ends.",
				["EL_ShockingChargeIndicator"] = "Your hits build charges. At full charge, your next hit unleashes a lightning shockwave.",
				["EL_SpiritAnimal"] = "Your bound creature's spirit fights beside you until this ends.",
				["EL_Vanished"] = "Enemies have lost sight of you. Your next hit from hiding is a sneak attack."
			};
			statusTooltips = new List<UITooltip>();
			Put(Section.Resources, CriticalHealth.Where((string type) => type.Contains("Regen")).ToArray());
			Put(Section.Resources, new string[44]
			{
				MagicEffectType.DayHealthRegen,
				MagicEffectType.DayStaminaRegen,
				MagicEffectType.NightStaminaRegenIncrease,
				MagicEffectType.RestingHealthRegen,
				MagicEffectType.LifeSteal,
				MagicEffectType.LifeStealLowHealth,
				MagicEffectType.EitrLeech,
				MagicEffectType.BloodDrinker,
				MagicEffectType.Bloodlust,
				MagicEffectType.LifeGainOnHit,
				MagicEffectType.HealthGainPerXDamageDone,
				MagicEffectType.HealthOnEitrUse,
				MagicEffectType.StaminaOnKill,
				MagicEffectType.StaminaReturnFromEitr,
				MagicEffectType.RunningOnEmpty,
				MagicEffectType.ConsumeEitrFirstForBloodCosts,
				MagicEffectType.ConvertEitrCostToStaminaCost,
				MagicEffectType.ReduceEitrCost,
				MagicEffectType.KillsReduceNextBloodCost,
				MagicEffectType.GainEitrWhenSacrificingHealth,
				MagicEffectType.Conduit,
				MagicEffectType.Kindling,
				MagicEffectType.PerfectDodge,
				MagicEffectType.PerfectDodgeGivesHealth,
				MagicEffectType.PerfectDodgeGivesStamina,
				MagicEffectType.PerfectDodgeGivesEitr,
				MagicEffectType.AdrenalineCharge,
				MagicEffectType.AdrenalineFrostWave,
				MagicEffectType.AdrenalineIncreasesHealthRegen,
				MagicEffectType.BurningAdrenaline,
				MagicEffectType.DamageTakenGivesAdrenaline,
				MagicEffectType.EitrUseGivesAdrenaline,
				MagicEffectType.GainAdrenalineFromHarvesting,
				MagicEffectType.GainAdrenalineWhenApplyingPoison,
				MagicEffectType.GainAdrenalineWhenSacrificingHealth,
				MagicEffectType.SummonBatWhenActivatingAdrenaline,
				MagicEffectType.UseAdrenalineAsStamina,
				MagicEffectType.StormFury,
				MagicEffectType.Everflow,
				MagicEffectType.AutoMead,
				MagicEffectType.InstantMead,
				MagicEffectType.DecreaseMeadCooldown,
				MagicEffectType.PotionEfficacy,
				MagicEffectType.DecreaseForsakenCooldown
			});
			Put(Section.Defense, new string[35]
			{
				MagicEffectType.ModifyArmorLowHealth,
				MagicEffectType.DayArmor,
				MagicEffectType.DamageReductionAtNight,
				MagicEffectType.ModifyBlockPowerLowHealth,
				MagicEffectType.ModifyParryLowHealth,
				MagicEffectType.AvoidDamageTaken,
				MagicEffectType.AvoidDamageTakenLowHealth,
				MagicEffectType.ReflectDamage,
				MagicEffectType.EitrShield,
				MagicEffectType.Warding,
				MagicEffectType.ElementalWarding,
				MagicEffectType.Coinplated,
				MagicEffectType.Immovable,
				MagicEffectType.BloodStaggerBlock,
				MagicEffectType.BloodBaseBlock,
				MagicEffectType.LifeGainOnBlock,
				MagicEffectType.StaminaGainOnBlock,
				MagicEffectType.EitrGainOnBlock,
				MagicEffectType.LuckyBlock,
				MagicEffectType.StaggerOnBlock,
				MagicEffectType.PhysToFireOnBlock,
				MagicEffectType.PhysToFrostOnBlock,
				MagicEffectType.PhysToPoisonOnBlock,
				MagicEffectType.PhysToLightningOnBlock,
				MagicEffectType.EitrWeave,
				MagicEffectType.OffSetAttack,
				MagicEffectType.Bulwark,
				MagicEffectType.Undying,
				MagicEffectType.ReduceFallDamage,
				MagicEffectType.RollCleanse,
				MagicEffectType.ForestsAid,
				MagicEffectType.IcyRetribution,
				MagicEffectType.Waterproof,
				MagicEffectType.Warmth,
				MagicEffectType.Paralyze
			});
			Put(Section.Movement, new string[8]
			{
				MagicEffectType.ModifyMovementSpeedLowHealth,
				MagicEffectType.NightCarryWeight,
				MagicEffectType.GainMaxCarryWeightFromRested,
				MagicEffectType.DoubleJump,
				MagicEffectType.StormRider,
				MagicEffectType.BurningSpeed,
				MagicEffectType.PerfectDodgeGivesSpeed,
				MagicEffectType.Trailblazer
			});
			Put(Section.Attacks, new string[45]
			{
				MagicEffectType.ModifyDamageLowHealth,
				MagicEffectType.IncreaseDamageDuringDaytime,
				MagicEffectType.IncreaseDamageDuringNighttime,
				MagicEffectType.ModifyAttackSpeed,
				MagicEffectType.ModifyAttackSpeedLowHealth,
				MagicEffectType.ModifyBackstab,
				MagicEffectType.ModifyStaggerDamage,
				MagicEffectType.ModifyStaggerDuration,
				MagicEffectType.StaggerOnDamageTaken,
				MagicEffectType.ChanceDoubleDamage,
				MagicEffectType.ChanceToCritOnHit,
				MagicEffectType.Executioner,
				MagicEffectType.Opportunist,
				MagicEffectType.Slow,
				MagicEffectType.ExplosiveArrows,
				MagicEffectType.QuickDraw,
				MagicEffectType.ModifyDrawStaminaUse,
				MagicEffectType.ModifyProjectileSpeed,
				MagicEffectType.ModifyMagicFireRate,
				MagicEffectType.ModifyFireRate,
				MagicEffectType.AmmoConservation,
				MagicEffectType.TripleBowShot,
				MagicEffectType.DoubleMagicShot,
				MagicEffectType.ChainLightning,
				MagicEffectType.FrostDamageAOE,
				MagicEffectType.StrikeCausesLightning,
				MagicEffectType.ShockingCharge,
				MagicEffectType.MeteorSummoner,
				MagicEffectType.CorpseRot,
				MagicEffectType.PoisonToTrueDamage,
				MagicEffectType.IncreaseAllPoisonDamageDone,
				MagicEffectType.EitrImbueAttack,
				MagicEffectType.Throwable,
				MagicEffectType.RecallWeapon,
				MagicEffectType.Apportation,
				MagicEffectType.HeadHunter,
				MagicEffectType.DodgeBuff,
				MagicEffectType.Bloodrage,
				MagicEffectType.SpellSword,
				MagicEffectType.ModifySummonDamage,
				MagicEffectType.ModifySummonHealth,
				MagicEffectType.BatteringRam,
				MagicEffectType.ModifyAttackStaminaUse,
				MagicEffectType.ModifyAttackEitrUse,
				MagicEffectType.ModifyAttackHealthUse
			});
			Put(Section.Skills, new string[7]
			{
				MagicEffectType.QuickLearner,
				MagicEffectType.Inspiration,
				MagicEffectType.IncreaseXPGainFromMovementPenalty,
				MagicEffectType.IncreaseHarvestXPGain,
				MagicEffectType.IncreaseRangedSkills,
				MagicEffectType.DayBlocker,
				MagicEffectType.NightBlocker
			});
			Put(Section.Utility, new string[22]
			{
				MagicEffectType.Luck,
				MagicEffectType.LuckyLoot,
				MagicEffectType.LuckyCraft,
				MagicEffectType.LuckWhileFishing,
				MagicEffectType.Riches,
				MagicEffectType.IncreaseMiningDrop,
				MagicEffectType.IncreaseTreeDrop,
				MagicEffectType.IncreaseHarvestDamage,
				MagicEffectType.BountifulHarvest,
				MagicEffectType.Mercenary,
				MagicEffectType.Wager,
				MagicEffectType.ModifyPickupRange,
				MagicEffectType.ModifyBuildDistance,
				MagicEffectType.ModifyDiscoveryRadius,
				MagicEffectType.DayDiscovery,
				MagicEffectType.ModifyWispRange,
				MagicEffectType.FreeBuild,
				MagicEffectType.Glowing,
				MagicEffectType.DvergerCirclet,
				MagicEffectType.Wishbone,
				MagicEffectType.Andvaranaut,
				MagicEffectType.ReduceFishingStaminaCost
			});
			Put(Section.Resources, new string[3]
			{
				MagicEffectType.LifeAfterKill,
				MagicEffectType.LifeSiphon,
				MagicEffectType.GiantsBlood
			});
			Put(Section.Defense, new string[3]
			{
				MagicEffectType.ReduceStaggerTaken,
				MagicEffectType.EitrBarrier,
				MagicEffectType.Vanish
			});
			Put(Section.Movement, new string[4]
			{
				MagicEffectType.ModifyGrapplePullSpeed,
				MagicEffectType.ModifyGrappleFlightTime,
				MagicEffectType.Frostwalker,
				MagicEffectType.Hexen
			});
			Put(Section.Attacks, new string[18]
			{
				MagicEffectType.CloseQuarter,
				MagicEffectType.Artillery,
				MagicEffectType.Assassin,
				MagicEffectType.CoilingVenom,
				MagicEffectType.ConjuredArrows,
				MagicEffectType.Earthshaker,
				MagicEffectType.EitrInfusion,
				MagicEffectType.ElementalArchery,
				MagicEffectType.Firebrand,
				MagicEffectType.HelsHarvest,
				MagicEffectType.KineticQuake,
				MagicEffectType.NineSteps,
				MagicEffectType.OverwhelmingLaunch,
				MagicEffectType.PiercingBolts,
				MagicEffectType.QuarryMark,
				MagicEffectType.ShieldThrow,
				MagicEffectType.Sniper,
				MagicEffectType.SpiritAnimal
			});
			Put(Section.Utility, new string[2]
			{
				MagicEffectType.GreenThumb,
				MagicEffectType.Prosperity
			});
			static void Put(Section section, string[] types)
			{
				foreach (string key in types)
				{
					Sections[key] = section;
				}
			}
		}

		[HarmonyPatch(typeof(TextsDialog), "UpdateTextsList")]
		[HarmonyPostfix]
		[HarmonyAfter(new string[] { "randyknapp.mods.epicloot" })]
		private static void ReplacePages(TextsDialog __instance)
		{
			if (!((Object)(object)Player.m_localPlayer == (Object)null) && !((Object)(object)MagicPages.instance == (Object)null))
			{
				List<TextInfo> obj = Texts.Invoke(__instance);
				string native = Localization.instance.Localize("$inventory_activeeffects");
				obj.RemoveAll((TextInfo text) => (object)text == MagicPages.instance.MagicEffectsPage || (!(text is MagicTextInfo) && text.m_topic == native));
				obj.Insert(0, (TextInfo)(object)(page ?? (page = new Page())));
			}
		}

		[HarmonyPatch(typeof(MagicPages), "Reset")]
		[HarmonyPostfix]
		private static void Hidden(MagicPages __instance)
		{
			if (!((Object)(object)shownOn != (Object)(object)__instance))
			{
				shownOn = null;
				shownRows = null;
				statusTooltips.Clear();
				if ((Object)(object)columns != (Object)null)
				{
					Object.Destroy((Object)(object)columns);
				}
				columns = null;
			}
		}

		[HarmonyPatch(typeof(MagicPages), "OnSelectText")]
		[HarmonyPostfix]
		private static void Opened(MagicPages __instance, MagicTextInfo text)
		{
			//IL_004e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0054: Expected O, but got Unknown
			if ((object)text != page || (Object)(object)shownOn != (Object)(object)__instance)
			{
				return;
			}
			ScrollRect val = ListScroll.Invoke(__instance.MagicPagesTextArea);
			GameObject val2 = ElementObject.Invoke(__instance.TitleElement);
			foreach (Transform item in (Transform)val.content)
			{
				Transform val3 = item;
				if ((Object)(object)((Component)val3).gameObject != (Object)(object)val2 && (Object)(object)((Component)val3).gameObject != (Object)(object)columns)
				{
					LayoutElement component = ((Component)val3).GetComponent<LayoutElement>();
					if (component == null || !component.ignoreLayout)
					{
						((Component)val3).gameObject.SetActive(false);
					}
				}
			}
			Canvas.ForceUpdateCanvases();
			LayoutRebuilder.ForceRebuildLayoutImmediate(val.content);
			val.StopMovement();
			val.verticalNormalizedPosition = 1f;
		}

		[HarmonyPatch(typeof(MagicPages), "Update")]
		[HarmonyPostfix]
		private static void Refresh(MagicPages __instance)
		{
			//IL_00b5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00cb: Expected O, but got Unknown
			//IL_0106: Unknown result type (might be due to invalid IL or missing references)
			//IL_010c: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)shownOn != (Object)(object)__instance || shownRows == null || Time.unscaledTime < nextRefresh || (Object)(object)Player.m_localPlayer == (Object)null)
			{
				return;
			}
			List<(string, string)> list = new List<(string, string)>();
			List<string> list2 = RowsWithTooltips(Player.m_localPlayer, list);
			List<MagicTextGroup> elements = __instance.MagicPagesTextArea.Elements;
			if (list2.Count == shownRows.Count && elements.Count == list2.Count && list.Count == statusTooltips.Count)
			{
				for (int i = 0; i < list2.Count; i++)
				{
					if (list2[i] != shownRows[i])
					{
						SetText((MagicTextElement)elements[i].Title, Shown(list2[i]));
					}
				}
				for (int j = 0; j < list.Count; j++)
				{
					statusTooltips[j].Set(list[j].Item1, list[j].Item2, (RectTransform)null, default(Vector2));
					if (list[j].Item2.Length == 0 && (Object)(object)ShownTooltip.Invoke() == (Object)(object)statusTooltips[j])
					{
						UITooltip.HideTooltip();
					}
				}
				shownRows = list2;
				nextRefresh = Time.unscaledTime + 0.5f;
			}
			else
			{
				__instance.MagicPagesTextArea.Clear();
				Fill(__instance, list2, list);
			}
			__instance.MagicPagesTextArea.ResizeOverlay();
			__instance.OnSearch(__instance.Search.Input.text);
		}

		private static void Fill(MagicPages pages, List<string> rows, List<(string Topic, string Text)> tooltips)
		{
			foreach (string row in rows)
			{
				pages.MagicPagesTextArea.Add(Shown(row), Array.Empty<string>());
			}
			Arrange(pages, rows, tooltips);
			shownRows = rows;
			nextRefresh = Time.unscaledTime + 0.5f;
		}

		private static void Arrange(MagicPages pages, List<string> rows, List<(string Topic, string Text)> tooltips)
		{
			//IL_0028: Unknown result type (might be due to invalid IL or missing references)
			//IL_0032: Expected O, but got Unknown
			//IL_0062: Unknown result type (might be due to invalid IL or missing references)
			//IL_0071: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fc: Unknown result type (might be due to invalid IL or missing references)
			//IL_01e3: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ea: Expected O, but got Unknown
			//IL_0291: Unknown result type (might be due to invalid IL or missing references)
			//IL_029b: Expected O, but got Unknown
			//IL_02d6: Unknown result type (might be due to invalid IL or missing references)
			//IL_02dc: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)columns == (Object)null)
			{
				columns = new GameObject("Active effects columns", new Type[1] { typeof(RectTransform) });
				columns.transform.SetParent(((Component)ListLayout.Invoke(pages.MagicPagesTextArea)).transform, false);
				((RectTransform)columns.transform).pivot = new Vector2(0.5f, 1f);
				HorizontalLayoutGroup obj = columns.AddComponent<HorizontalLayoutGroup>();
				((HorizontalOrVerticalLayoutGroup)obj).spacing = 24f;
				bool flag = (((HorizontalOrVerticalLayoutGroup)obj).childControlHeight = true);
				bool childControlWidth = (((HorizontalOrVerticalLayoutGroup)obj).childForceExpandWidth = flag);
				((HorizontalOrVerticalLayoutGroup)obj).childControlWidth = childControlWidth;
				((HorizontalOrVerticalLayoutGroup)obj).childForceExpandHeight = false;
				columns.AddComponent<ContentSizeFitter>().verticalFit = (FitMode)2;
				for (int i = 0; i < 2; i++)
				{
					GameObject val = new GameObject("Column", new Type[1] { typeof(RectTransform) });
					val.transform.SetParent(columns.transform, false);
					LayoutElement obj2 = val.AddComponent<LayoutElement>();
					float minWidth = (obj2.preferredWidth = 0f);
					obj2.minWidth = minWidth;
					obj2.flexibleWidth = 1f;
					VerticalLayoutGroup obj3 = val.AddComponent<VerticalLayoutGroup>();
					((HorizontalOrVerticalLayoutGroup)obj3).spacing = 2f;
					((LayoutGroup)obj3).childAlignment = (TextAnchor)0;
					flag = (((HorizontalOrVerticalLayoutGroup)obj3).childControlHeight = true);
					childControlWidth = (((HorizontalOrVerticalLayoutGroup)obj3).childForceExpandWidth = flag);
					((HorizontalOrVerticalLayoutGroup)obj3).childControlWidth = childControlWidth;
					((HorizontalOrVerticalLayoutGroup)obj3).childForceExpandHeight = false;
				}
			}
			columns.transform.SetAsLastSibling();
			int num2 = rows.Count;
			for (int j = 1; j < rows.Count; j++)
			{
				if (IsHeading(rows[j]) && Math.Max(j, rows.Count - j) < Math.Max(num2, rows.Count - num2))
				{
					num2 = j;
				}
			}
			List<MagicTextGroup> elements = pages.MagicPagesTextArea.Elements;
			for (int k = 0; k < elements.Count; k++)
			{
				MagicTextElement val2 = (MagicTextElement)elements[k].Title;
				val2.SetFontSize(16);
				val2.SetParent(columns.transform.GetChild((k >= num2) ? 1 : 0));
				if (IsHeading(rows[k]))
				{
					val2.SetAlignment((TextAnchor)6);
					ElementObject.Invoke(val2).AddComponent<LayoutElement>().minHeight = 34f;
				}
			}
			statusTooltips.Clear();
			GameObject tooltipPrefab = InventoryGui.instance.m_playerGrid.m_elementPrefab.GetComponent<UITooltip>().m_tooltipPrefab;
			for (int l = 0; l < tooltips.Count; l++)
			{
				GameObject obj4 = ElementObject.Invoke((MagicTextElement)elements[l + 1].Title);
				((Graphic)obj4.GetComponent<TMP_Text>()).raycastTarget = true;
				UITooltip val3 = obj4.AddComponent<UITooltip>();
				val3.m_tooltipPrefab = tooltipPrefab;
				val3.Set(tooltips[l].Topic, tooltips[l].Text, (RectTransform)null, default(Vector2));
				statusTooltips.Add(val3);
			}
		}

		private static string Shown(string row)
		{
			if (!IsHeading(row))
			{
				return "<indent=1em><line-indent=-1em>" + row;
			}
			return row;
		}

		private static bool IsHeading(string row)
		{
			return row.StartsWith($"<size={20}>", StringComparison.Ordinal);
		}

		internal static List<string> Rows(Player player)
		{
			return RowsWithTooltips(player, new List<(string, string)>());
		}

		private static List<string> RowsWithTooltips(Player player, List<(string Topic, string Text)> tooltips)
		{
			//IL_0074: Unknown result type (might be due to invalid IL or missing references)
			//IL_0079: Unknown result type (might be due to invalid IL or missing references)
			//IL_007c: Unknown result type (might be due to invalid IL or missing references)
			Dictionary<SkillType, Skill> dictionary = SkillData.Invoke(((Character)player).GetSkills());
			HashSet<SkillType> known = new HashSet<SkillType>(dictionary.Keys);
			try
			{
				return Build(player, known, tooltips);
			}
			finally
			{
				foreach (SkillType item in dictionary.Keys.Where((SkillType skill) => !known.Contains(skill)).ToList())
				{
					dictionary.Remove(item);
				}
			}
		}

		private static List<string> Build(Player player, HashSet<SkillType> known, List<(string Topic, string Text)> tooltips)
		{
			List<string> list = new List<string>();
			Dictionary<Section, List<string>> dictionary = new Dictionary<Section, List<string>>();
			MagicItemEffectDefinition val = default(MagicItemEffectDefinition);
			foreach (IGrouping<string, MagicItemEffect> item in (from effect in PlayerExtensions.GetAllActiveMagicEffects(player, (string)null)
				group effect by effect.EffectType).OrderBy<IGrouping<string, MagicItemEffect>, string>((IGrouping<string, MagicItemEffect> group) => group.Key, StringComparer.Ordinal))
			{
				if (!Included.Contains(item.Key) && MagicItemEffectDefinitions.TryGet(item.Key, ref val))
				{
					string text = Localization.instance.Localize(MagicItem.GetEffectText(val, PlayerExtensions.GetTotalActiveMagicEffectValue(player, item.Key, 1f, (ItemData)null)));
					bool? flag = Condition(player, item.Key);
					if (flag == true)
					{
						text += Muted(" (active now)");
					}
					else if (flag == false)
					{
						text = Muted(text + " (not now)");
					}
					Section value;
					Section key = (Sections.TryGetValue(item.Key, out value) ? value : Section.Other);
					if (!dictionary.TryGetValue(key, out var value2))
					{
						value2 = (dictionary[key] = new List<string>());
					}
					value2.Add(text);
				}
			}
			List<string> list3 = new List<string>();
			StatusRows(player, list3, tooltips);
			if (list3.Count > 0)
			{
				list.Add(Title("Status effects"));
				list.AddRange(list3);
			}
			foreach (Section value4 in Enum.GetValues(typeof(Section)))
			{
				List<string> list4 = new List<string>();
				switch (value4)
				{
				case Section.Resources:
					ResourceRows(player, list4);
					break;
				case Section.Defense:
					DefenseRows(player, list4);
					break;
				case Section.Movement:
					MovementRows(player, list4);
					break;
				case Section.Attacks:
					AttackRows(player, list4);
					break;
				case Section.Skills:
					SkillRows(player, known, list4);
					break;
				case Section.Utility:
					UtilityRows(player, list4);
					break;
				}
				if (dictionary.TryGetValue(value4, out var value3))
				{
					list4.AddRange(value3);
				}
				if (list4.Count != 0)
				{
					list.Add(Title(SectionNames[(int)value4]));
					list.AddRange(list4);
				}
			}
			return list;
		}

		private static void StatusRows(Player player, List<string> rows, List<(string Topic, string Text)> tooltips)
		{
			List<StatusEffect> list = new List<StatusEffect>();
			((Character)player).GetSEMan().GetHUDStatusEffects(list);
			foreach (StatusEffect item in list)
			{
				string text = Localization.instance.Localize(item.m_name);
				if (!string.IsNullOrWhiteSpace(text))
				{
					string iconText = item.GetIconText();
					rows.Add(string.IsNullOrEmpty(iconText) ? text : Row(text, iconText));
					tooltips.Add(Tooltip(text, Description(item)));
				}
			}
			foreach (Food food in player.GetFoods())
			{
				string text2 = Localization.instance.Localize(food.m_item.m_shared.m_name);
				rows.Add(Row(text2, Duration(food.m_time)));
				tooltips.Add(Tooltip(text2, FoodText(food)));
			}
			StatusEffect val = default(StatusEffect);
			float num = default(float);
			player.GetGuardianPowerHUD(ref val, ref num);
			if ((Object)(object)val != (Object)null)
			{
				string text3 = Localization.instance.Localize(val.m_name);
				rows.Add(Row("Forsaken power: " + text3, (num > 0f) ? ("ready in " + Duration(num)) : "ready"));
				tooltips.Add(Tooltip(text3, PowerText(player, val)));
			}
		}

		private static (string Topic, string Text) Tooltip(string name, string text)
		{
			if (text.Length != 0)
			{
				return (Topic: name, Text: text);
			}
			return (Topic: "", Text: "");
		}

		private static string GameStatusText(StatusEffect effect)
		{
			//IL_0227: Unknown result type (might be due to invalid IL or missing references)
			//IL_01c2: Unknown result type (might be due to invalid IL or missing references)
			//IL_01cc: Expected O, but got Unknown
			switch (((Object)effect).name)
			{
			case "Burning":
				return "Fire damage over time. Getting wet puts it out sooner.";
			case "CampFire":
				return "Eases the cold. Sit or take shelter here to rest.";
			case "Demister":
			{
				GameObject val = WispBall.Invoke((SE_Demister)effect);
				Demister val2 = (Object.op_Implicit((Object)(object)val) ? val.GetComponentInChildren<Demister>() : null);
				if (!Object.op_Implicit((Object)(object)val2))
				{
					return "Clears the mist around you.";
				}
				return "Clears the mist within " + val2.m_forceField.endRange.ToString("0.#", Numbers) + " m of you.";
			}
			case "EL_FrostWave":
			case "Frost":
				return $"Slows you by about {Mathf.RoundToInt((1f - ((SE_Frost)effect).m_minSpeedFactor) * 100f)}% at first, easing as it wears off. Any frost resistance prevents it.";
			case "Harpooned":
				return "A line ties you to whoever harpooned you and drags you back if you pull away.";
			case "Paralyze":
				return "You can't move until this ends.";
			case "Potion_BugRepellent":
				return "Deathsquitos flee from you instead of attacking.";
			case "Potion_tamer":
				return "Animals near you tame twice as fast.";
			case "Potion_TrollPheromones":
				return $"Trolls spawn more often where you are, with at least {Plugin.LovePotionMinimumTrollStars.Value} stars.";
			case "Smoked":
				return "You lose 2 health every second until you leave the smoke.";
			case "Spirit":
				return "Spirit damage over time.";
			default:
				return null;
			}
		}

		private static string Description(StatusEffect effect)
		{
			string text = effect.GetTooltipString();
			string text2 = GameStatusText(effect);
			if (text2 != null)
			{
				text = text2 + text.Substring(effect.m_tooltip.Length);
			}
			text = Localization.instance.Localize(StatusEffectDurationUnits.WithSeconds(text)).Trim();
			if (text.Length != 0 || !EpicLootStatusTexts.TryGetValue(((Object)effect).name, out var value))
			{
				return text;
			}
			return value;
		}

		private static string Detail(string label, string value)
		{
			return label + ": <color=#ffa500>" + value + "</color>";
		}

		private static string FoodText(Food food)
		{
			List<string> list = new List<string>();
			if (food.m_health > 0f)
			{
				list.Add(Detail("Health", Number(food.m_health, "0")));
			}
			if (food.m_stamina > 0f)
			{
				list.Add(Detail("Stamina", Number(food.m_stamina, "0")));
			}
			if (food.m_eitr > 0f)
			{
				list.Add(Detail("Eitr", Number(food.m_eitr, "0")));
			}
			if (food.m_item.m_shared.m_foodRegen > 0f)
			{
				list.Add(Detail("Healing", Number(food.m_item.m_shared.m_foodRegen) + " every 10 s"));
			}
			return string.Join("\n", list);
		}

		private static string PowerText(Player player, StatusEffect power)
		{
			List<string> list = new List<string>();
			string text = Description(power);
			if (text.Length > 0)
			{
				list.Add(text);
			}
			float totalActiveMagicEffectValue = PlayerExtensions.GetTotalActiveMagicEffectValue(player, MagicEffectType.DecreaseForsakenCooldown, 0.01f, (ItemData)null);
			if (power.m_cooldown > 0f)
			{
				list.Add(Detail("Recharge", Duration(power.m_cooldown * (1f - totalActiveMagicEffectValue))));
			}
			return string.Join("\n", list);
		}

		private static void ResourceRows(Player player, List<string> rows)
		{
			rows.Add(Row("Health", Number(((Character)player).GetHealth(), "0") + " / " + Number(((Character)player).GetMaxHealth(), "0")));
			SEMan sEMan = ((Character)player).GetSEMan();
			float num = 1f;
			sEMan.ModifyHealthRegen(ref num);
			float num2 = player.GetFoods().Sum((Food entry) => entry.m_item.m_shared.m_foodRegen);
			float value = ((num2 > 0f) ? ((num2 + PlayerExtensions.GetTotalActiveMagicEffectValue(player, MagicEffectType.AddHealthRegen, 1f, (ItemData)null)) * num) : 0f);
			if (num2 > 0f)
			{
				rows.Add(Row("Health regeneration", Number(value) + " every 10 s" + (Mathf.Approximately(num, 1f) ? "" : (" (" + Change(num) + ")"))));
			}
			float lowHealthPercentage = ModifyWithLowHealth.GetLowHealthPercentage(player);
			if (!Mathf.Approximately(lowHealthPercentage, ModifyWithLowHealth.LowHealthDefaultThreshold) || CriticalHealth.Any((string type) => PlayerExtensions.HasActiveMagicEffect(player, type)))
			{
				rows.Add(Row("Health Critical", "at " + Number(Mathf.Min(lowHealthPercentage, 1f) * 100f) + "% or less" + (ModifyWithLowHealth.PlayerHasLowHealth(player) ? " (now)" : "")));
			}
			rows.Add(Row("Stamina", Number(player.GetStamina(), "0") + " / " + Number(((Character)player).GetMaxStamina(), "0")));
			float num3 = 1f;
			sEMan.ModifyStaminaRegen(ref num3);
			if (!Mathf.Approximately(num3, 1f))
			{
				rows.Add(Row("Stamina regeneration", Change(num3)));
			}
			float num4 = StaminaRegenDelay.EffectiveDelay(player.m_staminaRegenDelay, player);
			if (!Mathf.Approximately(num4, player.m_staminaRegenDelay))
			{
				rows.Add(Row("Stamina regeneration delay", Number(num4, "0.##") + " s"));
			}
			float num5 = 1f;
			sEMan.ModifyEitrRegen(ref num5);
			if (((Character)player).GetMaxEitr() > 0f)
			{
				rows.Add(Row("Eitr", Number(player.GetEitr(), "0") + " / " + Number(((Character)player).GetMaxEitr(), "0")));
				if (!Mathf.Approximately(num5, 1f))
				{
					rows.Add(Row("Eitr regeneration", Change(num5)));
				}
			}
			if (((Character)player).GetMaxAdrenaline() > 0f)
			{
				rows.Add(Row("Adrenaline", Number(player.GetAdrenaline(), "0") + " / " + Number(((Character)player).GetMaxAdrenaline(), "0")));
			}
		}

		private static void DefenseRows(Player player, List<string> rows)
		{
			//IL_0022: Unknown result type (might be due to invalid IL or missing references)
			//IL_0027: Unknown result type (might be due to invalid IL or missing references)
			//IL_0047: Unknown result type (might be due to invalid IL or missing references)
			//IL_004c: Unknown result type (might be due to invalid IL or missing references)
			//IL_005c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0063: Expected O, but got Unknown
			//IL_006a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0092: Unknown result type (might be due to invalid IL or missing references)
			rows.Add(Row("Armor", Number(((Character)player).GetBodyArmor())));
			DamageModifiers damageModifiers = ((Character)player).GetDamageModifiers((WeakSpot)null);
			List<(float, List<string>)> list = new List<(float, List<string>)>();
			(DamageType, string)[] damageTypes = DamageTypes;
			DamageModifier val2 = default(DamageModifier);
			for (int i = 0; i < damageTypes.Length; i++)
			{
				(DamageType, string) tuple = damageTypes[i];
				DamageType item = tuple.Item1;
				string item2 = tuple.Item2;
				HitData val = new HitData();
				AddDamage(ref val.m_damage, item, 100f);
				IncomingPhysicalConversion.ModifyIncoming((Character)(object)player, val, false, false);
				ModifyResistance.ModifyIncoming((Character)(object)player, val);
				DamageReductionAtNight.ModifyIncoming((Character)(object)player, val);
				val.ApplyResistance(damageModifiers, ref val2);
				float taken = val.GetTotalDamage() / 100f;
				int num = list.FindIndex(((float Taken, List<string> Names) entry) => Mathf.Abs(entry.Taken - taken) < 0.0005f);
				if (num < 0)
				{
					list.Add((taken, new List<string> { item2 }));
				}
				else
				{
					list[num].Item2.Add(item2.ToLowerInvariant());
				}
			}
			foreach (var (num2, list2) in list)
			{
				if (!Mathf.Approximately(num2, 1f))
				{
					string text = ((list2.Count == 1) ? list2[0] : (string.Join(", ", list2.Take(list2.Count - 1)) + " and " + list2[list2.Count - 1]));
					rows.Add(Row(text + " damage taken", (num2 <= 0f) ? "immune" : Change(num2)));
				}
			}
			ItemData val3 = BlockedHits.CurrentBlocker((Humanoid)(object)player);
			if (val3 != null)
			{
				float skillFactor = ((Character)player).GetSkillFactor((SkillType)6);
				string text2 = Localization.instance.Localize(val3.m_shared.m_name);
				rows.Add(Row("Block armor", Number(val3.GetBlockPower(skillFactor)) + " (" + text2 + ")"));
				rows.Add(Row("Block force", Number(val3.GetDeflectionForce())));
				if (val3.m_shared.m_timedBlockBonus > 1f)
				{
					float num3 = BlockTimer.Invoke((Humanoid)(object)player);
					float value;
					float deflectionForce;
					try
					{
						BlockTimer.Invoke((Humanoid)(object)player) = 0f;
						value = val3.GetBlockPower(skillFactor) * val3.m_shared.m_timedBlockBonus;
						((Character)player).GetSEMan().ModifyTimedBlockBonus(ref value);
						deflectionForce = val3.GetDeflectionForce();
					}
					finally
					{
						BlockTimer.Invoke((Humanoid)(object)player) = num3;
					}
					rows.Add(Row("Parry armor", Number(value)));
					if (!Mathf.Approximately(deflectionForce, val3.GetDeflectionForce()))
					{
						rows.Add(Row("Parry force", Number(deflectionForce)));
					}
					rows.Add(Row("Parry window", Number(ParryWindow.Seconds((Humanoid)(object)player) + PlayerExtensions.GetTotalActiveMagicEffectValue(player, MagicEffectType.ModifyParryWindow, 1f, (ItemData)null) / 1000f, "0.###") + " s"));
					float num4 = BlockStamina(player, ((Humanoid)player).m_perfectBlockStaminaDrain);
					if (num4 != 0f)
					{
						rows.Add(Row("Parry stamina", Number(num4)));
					}
				}
				rows.Add(Row("Block stamina", "up to " + Number(BlockStamina(player, ((Humanoid)player).m_blockStaminaDrain)) + " per hit"));
			}
			rows.Add(Row("Stagger limit", Number(((Character)player).GetMaxHealth() * ((Character)player).m_staggerDamageFactor, "0")));
			float num5 = 1f;
			((Character)player).GetSEMan().ModifyStagger(1f, ref num5);
			if (!Mathf.Approximately(num5, 1f))
			{
				rows.Add(Row("Stagger taken", Change(num5)));
			}
		}

		private static void MovementRows(Player player, List<string> rows)
		{
			//IL_001c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0067: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bb: Unknown result type (might be due to invalid IL or missing references)
			//IL_017b: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d4: Unknown result type (might be due to invalid IL or missing references)
			SEMan sEMan = ((Character)player).GetSEMan();
			float num = ((Character)player).m_speed * JogFactor((Character)(object)player);
			sEMan.ApplyStatusEffectSpeedMods(ref num, Vector3.zero);
			if (!Mathf.Approximately(num, ((Character)player).m_speed))
			{
				rows.Add(Row("Movement speed", Change(num / ((Character)player).m_speed)));
			}
			float num2 = ((Character)player).m_runSpeed * RunFactor((Character)(object)player);
			sEMan.ApplyStatusEffectSpeedMods(ref num2, Vector3.zero);
			if (!Mathf.Approximately(num2, ((Character)player).m_runSpeed))
			{
				rows.Add(Row("Running speed", Change(num2 / ((Character)player).m_runSpeed)));
			}
			Vector3 val = default(Vector3);
			((Vector3)(ref val))..ctor(0f, ((Character)player).m_jumpForce, 0f);
			sEMan.ApplyStatusEffectJumpMods(ref val);
			if (!Mathf.Approximately(val.y, ((Character)player).m_jumpForce))
			{
				rows.Add(Row("Jump strength", Change(val.y / ((Character)player).m_jumpForce)));
			}
			rows.Add(Row("Carried weight", Number(((Humanoid)player).GetInventory().GetTotalWeight()) + " / " + Number(MaxCarryWeight(player))));
			Skills skills = ((Character)player).GetSkills();
			float equipmentMovementModifier = ((Character)player).GetEquipmentMovementModifier();
			float num3 = player.m_runStaminaDrain * Mathf.Lerp(1f, 0.5f, skills.GetSkillFactor((SkillType)102));
			num3 -= num3 * equipmentMovementModifier;
			num3 += num3 * ((Character)player).GetEquipmentRunStaminaModifier();
			sEMan.ModifyRunStaminaDrain(num3, ref num3, Vector3.zero, true);
			rows.Add(Row("Running stamina", Number(num3) + " per second"));
			float num4 = ((Character)player).m_jumpStaminaUsage - ((Character)player).m_jumpStaminaUsage * equipmentMovementModifier + ((Character)player).m_jumpStaminaUsage * ((Character)player).GetEquipmentJumpStaminaModifier();
			sEMan.ModifyJumpStaminaUsage(num4, ref num4, true);
			rows.Add(Row("Jump stamina", Number(num4)));
			rows.Add(Row("Dodge stamina", Number(DodgeStamina(player))));
			float num5 = Mathf.Lerp(player.m_swimStaminaDrainMinSkill, player.m_swimStaminaDrainMaxSkill, skills.GetSkillFactor((SkillType)103));
			num5 += num5 * ((Character)player).GetEquipmentSwimStaminaModifier();
			sEMan.ModifySwimStaminaUsage(num5, ref num5, true);
			rows.Add(Row("Swimming stamina", Number(num5) + " per second"));
			float num6 = player.m_sneakStaminaDrain * Mathf.Lerp(1f, 0.25f, Mathf.Pow(skills.GetSkillFactor((SkillType)101), 0.5f));
			num6 += num6 * ((Character)player).GetEquipmentSneakStaminaModifier();
			sEMan.ModifySneakStaminaUsage(num6, ref num6, true);
			rows.Add(Row("Sneaking stamina", Number(num6) + " per second"));
			float num7 = 1f;
			sEMan.ModifyNoise(1f, ref num7);
			if (!Mathf.Approximately(num7, 1f))
			{
				rows.Add(Row("Noise", Change(num7)));
			}
			float num8 = 1f;
			sEMan.ModifyStealth(1f, ref num8);
			if (!Mathf.Approximately(num8, 1f))
			{
				rows.Add(Row("Stealth", Change(num8)));
			}
			float equipmentHeatResistanceModifier = ((Character)player).GetEquipmentHeatResistanceModifier();
			if (equipmentHeatResistanceModifier != 0f)
			{
				rows.Add(Row("Heat resistance", Change(1f + equipmentHeatResistanceModifier)));
			}
		}

		private static void AttackRows(Player player, List<string> rows)
		{
			//IL_0050: Unknown result type (might be due to invalid IL or missing references)
			//IL_0055: Unknown result type (might be due to invalid IL or missing references)
			//IL_005f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0064: Unknown result type (might be due to invalid IL or missing references)
			//IL_006e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0073: Unknown result type (might be due to invalid IL or missing references)
			ItemData currentWeapon = ((Humanoid)player).GetCurrentWeapon();
			if (currentWeapon == null)
			{
				return;
			}
			ItemData item = WeaponTooltipRange.Item;
			HandOverride.Hands active = HandOverride.Active;
			try
			{
				WeaponTooltipRange.Item = currentWeapon;
				WeaponTooltip.AsIfEquipped(currentWeapon);
				ItemData ammunition;
				bool conjured;
				(DamageTypes, DamageTypes) tuple = WeaponDamagePreview.Range(player, currentWeapon, out ammunition, out conjured);
				List<string> list = new List<string>();
				(DamageType, string)[] damageTypes = DamageTypes;
				for (int i = 0; i < damageTypes.Length; i++)
				{
					(DamageType, string) tuple2 = damageTypes[i];
					DamageType item2 = tuple2.Item1;
					string item3 = tuple2.Item2;
					float num = Damage(tuple.Item1, item2);
					float num2 = Damage(tuple.Item2, item2);
					if (num > 0f || num2 > 0f)
					{
						list.Add("<nobr>" + Number(num, "0") + "-" + Number(num2, "0") + " " + item3.ToLowerInvariant() + "</nobr>");
					}
				}
				string text = Localization.instance.Localize(currentWeapon.m_shared.m_name) + (conjured ? " with conjured arrows" : ((ammunition != null) ? (" with " + Localization.instance.Localize(ammunition.m_shared.m_name)) : ""));
				rows.Add(Row("Hit damage" + WeaponDamagePreview.PerProjectile(player, currentWeapon), ((list.Count > 0) ? string.Join(", ", list) : "none") + " (" + text + ")"));
			}
			finally
			{
				HandOverride.Active = active;
				WeaponTooltipRange.Item = item;
			}
		}

		private unsafe static void SkillRows(Player player, HashSet<SkillType> known, List<string> rows)
		{
			//IL_005b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0060: Unknown result type (might be due to invalid IL or missing references)
			//IL_0063: Unknown result type (might be due to invalid IL or missing references)
			//IL_0074: Unknown result type (might be due to invalid IL or missing references)
			//IL_0083: Unknown result type (might be due to invalid IL or missing references)
			Skills skills = ((Character)player).GetSkills();
			Dictionary<SkillType, Skill> dictionary = SkillData.Invoke(skills);
			List<(string, string)> list = new List<(string, string)>();
			foreach (SkillType item in skills.m_skills.Select((SkillDef definition) => definition.m_skill).Union(known).Distinct())
			{
				float num = (known.Contains(item) ? dictionary[item].m_level : 0f);
				float num2 = skills.GetSkillFactor(item) * 100f;
				float num3 = num2 - Mathf.Min(Mathf.Floor(num), 100f);
				if (!(Mathf.Abs(num3) < 0.5f))
				{
					list.Add((Localization.instance.Localize("$skill_" + ((object)(*(SkillType*)(&item))/*cast due to .constrained prefix*/).ToString().ToLowerInvariant()), Number(num2, "0") + " (" + ((num3 > 0f) ? "+" : "") + Number(num3, "0") + ")"));
				}
			}
			foreach (var item2 in list.OrderBy<(string, string), string>(((string Name, string Value) entry) => entry.Name, StringComparer.CurrentCulture))
			{
				rows.Add(Row(item2.Item1, item2.Item2));
			}
		}

		private static void UtilityRows(Player player, List<string> rows)
		{
			rows.Add(Row("Comfort", player.GetComfortLevel().ToString(Numbers)));
			float totalActiveMagicEffectValue = PlayerExtensions.GetTotalActiveMagicEffectValue(player, MagicEffectType.SailingSpeed, 1f, (ItemData)null);
			float totalActiveMagicEffectValue2 = PlayerExtensions.GetTotalActiveMagicEffectValue(player, MagicEffectType.DaySailingSpeed, 1f, (ItemData)null);
			if (!(totalActiveMagicEffectValue <= 0f) || !(totalActiveMagicEffectValue2 <= 0f))
			{
				bool flag = EnvMan.IsDay();
				float num = ((1f + totalActiveMagicEffectValue * 0.01f) * (1f + totalActiveMagicEffectValue2 * 0.01f) - 1f) * 100f;
				if (Mathf.Approximately(num, totalActiveMagicEffectValue))
				{
					rows.Add(Row("Helmsman's sailing and rowing speed", "+" + EffectTooltips.SailingSpeedText(totalActiveMagicEffectValue) + "%"));
					return;
				}
				SailingRow("Helmsman's daytime sailing and rowing speed", num, flag);
				SailingRow("Helmsman's nighttime sailing and rowing speed", totalActiveMagicEffectValue, !flag);
			}
			void SailingRow(string label, float forcePercent, bool active)
			{
				if (forcePercent != 0f)
				{
					string text = "+" + EffectTooltips.SailingSpeedText(forcePercent) + "%";
					rows.Add(active ? (Row(label, text) + Muted(" (active now)")) : Muted(label + " " + text + " (not now)"));
				}
			}
		}

		private static float BlockStamina(Player player, float drain)
		{
			drain += drain * ((Character)player).GetEquipmentBlockStaminaModifier();
			((Character)player).GetSEMan().ModifyBlockStaminaUsage(drain, ref drain, false);
			return drain;
		}

		private static float MaxCarryWeight(Player player)
		{
			float carryWeightRate = Game.m_carryWeightRate;
			Game.m_carryWeightRate = 1f;
			try
			{
				return player.GetMaxCarryWeight();
			}
			finally
			{
				Game.m_carryWeightRate = carryWeightRate;
			}
		}

		private static bool? Condition(Player player, string type)
		{
			if (CriticalHealth.Contains(type))
			{
				return ModifyWithLowHealth.PlayerHasLowHealth(player);
			}
			if (Daytime.Contains(type))
			{
				return EnvMan.IsDay();
			}
			if (Nighttime.Contains(type))
			{
				return EnvMan.IsNight();
			}
			if (WhileRested.Contains(type))
			{
				return ((Character)player).GetSEMan().HaveStatusEffect(SEMan.s_statusEffectRested);
			}
			return null;
		}

		private static void AddDamage(ref DamageTypes damage, DamageType type, float amount)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0003: Invalid comparison between Unknown and I4
			//IL_0028: Unknown result type (might be due to invalid IL or missing references)
			//IL_002e: Invalid comparison between Unknown and I4
			//IL_0005: Unknown result type (might be due to invalid IL or missing references)
			//IL_0007: Unknown result type (might be due to invalid IL or missing references)
			//IL_001d: Expected I4, but got Unknown
			//IL_0030: Unknown result type (might be due to invalid IL or missing references)
			//IL_0036: Invalid comparison between Unknown and I4
			//IL_001d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0020: Invalid comparison between Unknown and I4
			//IL_0038: Unknown result type (might be due to invalid IL or missing references)
			//IL_003e: Invalid comparison between Unknown and I4
			//IL_0022: Unknown result type (might be due to invalid IL or missing references)
			//IL_0025: Invalid comparison between Unknown and I4
			if ((int)type <= 64)
			{
				switch (type - 1)
				{
				case 0:
					damage.m_blunt += amount;
					return;
				case 1:
					damage.m_slash += amount;
					return;
				case 3:
					damage.m_pierce += amount;
					return;
				case 2:
					return;
				}
				if ((int)type != 32)
				{
					if ((int)type == 64)
					{
						damage.m_frost += amount;
					}
				}
				else
				{
					damage.m_fire += amount;
				}
			}
			else if ((int)type != 128)
			{
				if ((int)type != 256)
				{
					if ((int)type == 512)
					{
						damage.m_spirit += amount;
					}
				}
				else
				{
					damage.m_poison += amount;
				}
			}
			else
			{
				damage.m_lightning += amount;
			}
		}

		private static float Damage(DamageTypes damage, DamageType type)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0003: Invalid comparison between Unknown and I4
			//IL_0029: Unknown result type (might be due to invalid IL or missing references)
			//IL_002f: Invalid comparison between Unknown and I4
			//IL_0005: Unknown result type (might be due to invalid IL or missing references)
			//IL_0007: Unknown result type (might be due to invalid IL or missing references)
			//IL_001d: Expected I4, but got Unknown
			//IL_0066: Unknown result type (might be due to invalid IL or missing references)
			//IL_0031: Unknown result type (might be due to invalid IL or missing references)
			//IL_0037: Invalid comparison between Unknown and I4
			//IL_0043: Unknown result type (might be due to invalid IL or missing references)
			//IL_004a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0051: Unknown result type (might be due to invalid IL or missing references)
			//IL_001d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0020: Invalid comparison between Unknown and I4
			//IL_006d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0039: Unknown result type (might be due to invalid IL or missing references)
			//IL_003f: Invalid comparison between Unknown and I4
			//IL_0058: Unknown result type (might be due to invalid IL or missing references)
			//IL_0022: Unknown result type (might be due to invalid IL or missing references)
			//IL_0025: Invalid comparison between Unknown and I4
			//IL_0074: Unknown result type (might be due to invalid IL or missing references)
			//IL_005f: Unknown result type (might be due to invalid IL or missing references)
			if ((int)type <= 64)
			{
				switch (type - 1)
				{
				default:
					if ((int)type != 32)
					{
						if ((int)type != 64)
						{
							break;
						}
						return damage.m_frost;
					}
					return damage.m_fire;
				case 0:
					return damage.m_blunt;
				case 1:
					return damage.m_slash;
				case 3:
					return damage.m_pierce;
				case 2:
					break;
				}
			}
			else
			{
				if ((int)type == 128)
				{
					return damage.m_lightning;
				}
				if ((int)type == 256)
				{
					return damage.m_poison;
				}
				if ((int)type == 512)
				{
					return damage.m_spirit;
				}
			}
			return 0f;
		}

		private static string Title(string text)
		{
			return string.Format("<size={0}><color={1}>{2}</color></size>", 20, "#ffa626", text);
		}

		private static string Row(string label, string value)
		{
			return label + " <color=#ffa500>" + value + "</color>";
		}

		private static string Muted(string text)
		{
			return "<color=#9a9a9a>" + text + "</color>";
		}

		private static string Number(float value, string format = "0.#")
		{
			return value.ToString(format, Numbers);
		}

		private static string Change(float multiplier)
		{
			float num = (multiplier - 1f) * 100f;
			if (Mathf.Abs(num) < 0.05f)
			{
				return "0%";
			}
			return ((num > 0f) ? "+" : "") + Number(num) + "%";
		}

		private static string Duration(float seconds)
		{
			TimeSpan timeSpan = TimeSpan.FromSeconds(Mathf.CeilToInt(Mathf.Max(seconds, 0f)));
			if (!(timeSpan.TotalHours >= 1.0))
			{
				return $"{timeSpan.Minutes}:{timeSpan.Seconds:00}";
			}
			return $"{(int)timeSpan.TotalHours}:{timeSpan.Minutes:00}:{timeSpan.Seconds:00}";
		}
	}
	internal static class AffixDisplayValues
	{
		private static readonly Regex Arguments = new Regex("\\{(\\d+)(?::([^}]+))?\\}");

		internal static readonly Regex Numbers = new Regex("(?<![\\w#.])[+-]?\\d+(?:[.,]\\d+)?(?:[Ee][+-]?\\d+)?(?![\\w.])");

		private static readonly FieldRef<bool> InConversion = AccessTools.StaticFieldRefAccess<bool>(AccessTools.Field(typeof(AddSkillLevel_Skills_GetSkillFactor_Patch), "_inSkillsAsSkills"));

		internal static bool IsDropCount(string effectType)
		{
			if (!(effectType == MagicEffectType.IncreaseMiningDrop))
			{
				return effectType == MagicEffectType.IncreaseTreeDrop;
			}
			return true;
		}

		internal static void Initialize()
		{
			string[] array = new string[2]
			{
				MagicEffectType.IncreaseMiningDrop,
				MagicEffectType.IncreaseTreeDrop
			};
			for (int i = 0; i < array.Length; i++)
			{
				MagicItem.RegisterDisplayValues(array[i], (Func<float, object[]>)((float value) => new object[1] { Math.Max(0, (int)value) }));
			}
			MagicItem.RegisterDisplayValues(MagicEffectType.BlockAsDodgeAsBlock, (Func<float, object[]>)delegate(float value)
			{
				Player localPlayer = Player.m_localPlayer;
				return ((Object)(object)localPlayer == (Object)null) ? new object[3] { value, 0f, 0f } : new object[3]
				{
					value,
					(float)Converted(localPlayer, (SkillType)108, value),
					(float)Converted(localPlayer, (SkillType)6, value)
				};
			});
			MagicItem.RegisterDisplayValues(MagicEffectType.BlockAsWoodCuttingAndPickaxes, (Func<float, object[]>)delegate(float value)
			{
				Player localPlayer = Player.m_localPlayer;
				return new object[2]
				{
					value,
					(!((Object)(object)localPlayer == (Object)null)) ? (Converted(localPlayer, (SkillType)13, value) + Converted(localPlayer, (SkillType)12, value)) : 0
				};
			});
		}

		private static int Converted(Player player, SkillType source, float value)
		{
			//IL_001e: Unknown result type (might be due to invalid IL or missing references)
			bool flag = InConversion.Invoke();
			InConversion.Invoke() = true;
			try
			{
				return (int)(((Character)player).GetSkills().GetSkillFactor(source) * value);
			}
			finally
			{
				InConversion.Invoke() = flag;
			}
		}

		internal static string Format(object value, string preferredFormat, CultureInfo culture)
		{
			if (value is float num)
			{
				return ((double)num).ToString("0.##", culture);
			}
			if (!(value is IFormattable formattable))
			{
				return value.ToString();
			}
			return formattable.ToString(preferredFormat, culture);
		}

		internal static string Number(string effectType, float value)
		{
			if (!IsDropCount(effectType))
			{
				return Format(value, null, CultureInfo.CurrentCulture);
			}
			return Math.Max(0, (int)value).ToString(CultureInfo.CurrentCulture);
		}

		internal static string Generic(string effectType, string token)
		{
			float result;
			return Numbers.Replace(token, (Match match) => (!float.TryParse(match.Value.Replace(',', '.'), NumberStyles.Float, CultureInfo.InvariantCulture, out result)) ? match.Value : Number(effectType, result));
		}

		internal static object[] FormatArguments(string template, object[] arguments)
		{
			object[] array = (object[])arguments.Clone();
			foreach (Match item in Arguments.Matches(Localization.instance.Localize(template)))
			{
				int num = int.Parse(item.Groups[1].Value, CultureInfo.InvariantCulture);
				if (num < arguments.Length && arguments[num] is float)
				{
					array[num] = Format(arguments[num], item.Groups[2].Success ? item.Groups[2].Value : null, CultureInfo.CurrentCulture);
				}
			}
			return array;
		}
	}
	[HarmonyPatch(typeof(MagicItem), "FormatDisplayText")]
	internal static class AffixTextFormatting
	{
		private static void Prefix(string displayText, ref object[] args)
		{
			args = AffixDisplayValues.FormatArguments(displayText, args);
		}
	}
	[HarmonyPatch(typeof(MagicItem), "GetEffectTextGeneric")]
	internal static class AppliedAffixPreview
	{
		private static readonly Func<string, float, object[]> DisplayArguments = AccessTools.MethodDelegate<Func<string, float, object[]>>(AccessTools.Method(typeof(MagicItem), "GetDisplayArgs", (Type[])null, (Type[])null), (object)null, true);

		private static readonly Func<string, object[], string> FormatText = AccessTools.MethodDelegate<Func<string, object[], string>>(AccessTools.Method(typeof(MagicItem), "FormatDisplayText", (Type[])null, (Type[])null), (object)null, true);

		private static bool Prefix(MagicItemEffectDefinition effectDef, ref string valueToken, ref string __result)
		{
			List<Match> list = AffixDisplayValues.Numbers.Matches(valueToken).Cast<Match>().ToList();
			List<float> list2 = new List<float>();
			foreach (Match item in list)
			{
				if (float.TryParse(item.Value.Replace(',', '.'), NumberStyles.Float, CultureInfo.InvariantCulture, out var result))
				{
					list2.Add(result);
				}
			}
			if (list2.Count == 0 || list2.Count != list.Count)
			{
				valueToken = AffixDisplayValues.Generic(effectDef.Type, valueToken);
				return true;
			}
			object[] array = DisplayArguments(effectDef.Type, 1f);
			object[] array2 = DisplayArguments(effectDef.Type, 2f);
			List<object[]> perValue = list2.Select((float value) => DisplayArguments(effectDef.Type, value)).ToList();
			object[] array3 = new object[array.Length];
			string input = valueToken;
			for (int i = 0; i < array3.Length; i++)
			{
				if ((i < array2.Length && object.Equals(array[i], array2[i])) || perValue.Any((object[] shown) => i >= shown.Length))
				{
					array3[i] = array[i];
					continue;
				}
				int next = 0;
				array3[i] = AffixDisplayValues.Numbers.Replace(input, (Match match) => AffixDisplayValues.Format(perValue[next++][i], null, CultureInfo.CurrentCulture));
			}
			__result = FormatText(effectDef.DisplayText, array3);
			return false;
		}
	}
	[HarmonyPatch(typeof(MagicItem), "GetEffectDescription")]
	internal static class AppliedAffixDescription
	{
		private static void Postfix(MagicItemEffectDefinition effectDef, float value, ref string __result)
		{
			__result = __result.Replace("<b><color=yellow>" + value.ToString("0.#", CultureInfo.CurrentCulture) + "</color></b>", "<b><color=yellow>" + AffixDisplayValues.Number(effectDef.Type, value) + "</color></b>");
		}
	}
	[HarmonyPatch]
	internal static class AffixReplacements
	{
		[HarmonyPatch(typeof(MagicItemEffectRequirements), "CheckItemTypeRequirements")]
		private static class SocketedBloodletting
		{
			private static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instructions)
			{
				MethodInfo methodInfo = AccessTools.Method(typeof(ItemDataExtensions), "HasMagicEffect", (Type[])null, (Type[])null);
				List<CodeInstruction> list = instructions.ToList();
				int num = 0;
				for (int i = 1; i < list.Count; i++)
				{
					if (CodeInstructionExtensions.Calls(list[i], methodInfo) && !(list[i - 1].opcode != OpCodes.Ldc_I4_0))
					{
						list[i - 1].opcode = OpCodes.Ldc_I4_1;
						num++;
					}
				}
				if (num != 1)
				{
					throw new InvalidOperationException("Expected one rolled-only Bloodletting check in EpicLoot's item requirements.");
				}
				return list;
			}
		}

		private static readonly MethodInfo Available = AccessTools.Method(typeof(MagicItemEffectDefinitions), "GetAvailableEffects", (Type[])null, (Type[])null);

		private static IEnumerable<MethodBase> TargetMethods()
		{
			yield return AccessTools.Method(typeof(AugmentHelper), "GetAvailableAugments", (Type[])null, (Type[])null);
			yield return AccessTools.Method(typeof(LootRoller), "RollAugmentEffects", (Type[])null, (Type[])null);
			yield return AccessTools.Method(typeof(EnchantingUIController), "GetApplyableRunesforItem", (Type[])null, (Type[])null);
		}

		private static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instructions, MethodBase __originalMethod)
		{
			int replacements = 0;
			foreach (CodeInstruction instruction in instructions)
			{
				if (CodeInstructionExtensions.Calls(instruction, Available))
				{
					bool augmentHelper = __originalMethod.DeclaringType == typeof(AugmentHelper);
					bool runeEtch = __originalMethod.Name == "GetApplyableRunesforItem";
					CodeInstruction val = new CodeInstruction(augmentHelper ? OpCodes.Ldarg_0 : ((__originalMethod.DeclaringType == typeof(LootRoller)) ? OpCodes.Ldarg_2 : OpCodes.Ldarg_1), (object)null);
					val.labels.AddRange(instruction.labels);
					instruction.labels.Clear();
					yield return val;
					if (augmentHelper)
					{
						yield return new CodeInstruction(OpCodes.Ldfld, (object)AccessTools.Field(typeof(AugmentRecipe), "EffectIndex"));
					}
					instruction.operand = AccessTools.Method(typeof(AffixReplacements), runeEtch ? "RuneEffects" : "AugmentEffects", (Type[])null, (Type[])null);
					replacements++;
				}
				yield return instruction;
			}
			if (replacements != 1)
			{
				throw new InvalidOperationException("Expected one replacement effect check in " + __originalMethod.Name);
			}
		}

		internal static List<MagicItemEffectDefinition> AugmentEffects(ItemData item, MagicItem magicItem, int ignoredIndex, bool lootRoll, bool augmentRoll, bool runeRoll, int selectedIndex)
		{
			MagicItem magicItem2 = ItemDataExtensions.GetMagicItem(item);
			List<SocketedEffect> sockets = magicItem.Sockets;
			List<SocketedEffect> sockets2 = magicItem2?.Sockets;
			List<SocketedEffect> sockets3 = (magicItem.Sockets = new List<SocketedEffect>());
			if (magicItem2 != null && magicItem2 != magicItem)
			{
				magicItem2.Sockets = sockets3;
			}
			try
			{
				return ReplacementEffects(item, magicItem, selectedIndex, lootRoll: true, augmentRoll: true, runeRoll: false, selectedIndex);
			}
			finally
			{
				magicItem.Sockets = sockets;
				if (magicItem2 != null && magicItem2 != magicItem)
				{
					magicItem2.Sockets = sockets2;
				}
			}
		}

		internal static List<MagicItemEffectDefinition> ReplacementEffects(ItemData item, MagicItem magicItem, int ignoredIndex, bool lootRoll, bool augmentRoll, bool runeRoll, int selectedIndex)
		{
			string selectedType = ((selectedIndex >= 0 && selectedIndex < magicItem.Effects.Count) ? magicItem.Effects[selectedIndex].EffectType : null);
			List<MagicItemEffectDefinition> availableEffects = MagicItemEffectDefinitions.GetAvailableEffects(item, magicItem, selectedIndex, lootRoll, augmentRoll, runeRoll);
			availableEffects.RemoveAll((MagicItemEffectDefinition definition) => definition.Type != selectedType && SocketConflicts.ReplacementConflict(definition.Type, magicItem, selectedIndex) != null);
			if (selectedType != null && MagicItemEffectDefinitions.IsValuelessEffect(selectedType))
			{
				availableEffects.RemoveAll((MagicItemEffectDefinition definition) => definition.Type == selectedType);
			}
			return availableEffects;
		}

		private static List<MagicItemEffectDefinition> RuneEffects(ItemData item, MagicItem magicItem, int ignoredIndex, bool lootRoll, bool augmentRoll, bool runeRoll, string selectedEffect)
		{
			return ReplacementEffects(item, magicItem, ignoredIndex, lootRoll, augmentRoll, runeRoll, magicItem.Effects.FindIndex((MagicItemEffect effect) => effect.EffectType == selectedEffect));
		}

		internal static bool PreservesRequirements(ItemData item, MagicItem magicItem, MagicItemEffectDefinition candidate, string replacedType)
		{
			List<string> first = magicItem.Effects.Select((MagicItemEffect effect) => effect.EffectType).ToList();
			List<string> second = (from effect in magicItem.GetSocketedEffects()
				select effect.EffectType).ToList();
			HashSet<string> after = new HashSet<string>(first.Concat(second)) { candidate.Type };
			HashSet<string> before = new HashSet<string>(first.Concat(second));
			if (replacedType != null)
			{
				before.Add(replacedType);
			}
			if (!Satisfied(item, candidate.Type, after))
			{
				return false;
			}
			return first.Concat(from socket in magicItem.Sockets
				where socket?.Effect != null && (int)socket.ShardType == 97
				select socket.Effect.EffectType).Distinct().All((string effectType) => Satisfied(item, effectType, after) || !Satisfied(item, effectType, before));
		}

		private static bool Satisfied(ItemData item, string effectType, HashSet<string> present)
		{
			MagicItemEffectDefinition val = default(MagicItemEffectDefinition);
			if (!MagicItemEffectDefinitions.TryGet(effectType, ref val))
			{
				return true;
			}
			MagicItemEffectRequirements requirements = val.Requirements;
			Attack attack = item.m_shared.m_attack;
			Attack secondaryAttack = item.m_shared.m_secondaryAttack;
			bool flag = attack.m_attackHealth > 0f || secondaryAttack.m_attackHealth > 0f || attack.m_attackHealthPercentage > 0f || secondaryAttack.m_attackHealthPercentage > 0f || present.Contains(MagicEffectType.Bloodlust);
			if (requirements.ItemUsesHealthOnAttack.HasValue && requirements.ItemUsesHealthOnAttack.Value != flag)
			{
				return false;
			}
			return requirements.MustHaveEffectTypes.All((string required) => present.Contains(required) || (required == MagicEffectType.Throwable && (int)item.m_shared.m_skillType == 5));
		}
	}
	[HarmonyPatch(typeof(Character), "UpdateWalking")]
	internal static class AirborneIceMovement
	{
		private static bool OnSurface(bool slippery, Character character)
		{
			if (slippery)
			{
				if (character.IsPlayer())
				{
					return character.IsOnGround();
				}
				return true;
			}
			return false;
		}

		private static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instructions)
		{
			//IL_0085: Unknown result type (might be due to invalid IL or missing references)
			//IL_008b: Expected O, but got Unknown
			//IL_00dc: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e2: Expected O, but got Unknown
			List<CodeInstruction> list = instructions.ToList();
			FieldInfo slipping = AccessTools.Field(typeof(Character), "m_slipping");
			(CodeInstruction instruction, int index)[] array = (from entry in list.Select((CodeInstruction instruction, int index) => (instruction: instruction, index: index))
				where CodeInstructionExtensions.StoresField(entry.instruction, slipping)
				select entry).ToArray();
			if (array.Length != 1)
			{
				throw new InvalidOperationException("Character's walking slipping-state assignment changed.");
			}
			int item = array[0].index;
			CodeInstruction val = new CodeInstruction(OpCodes.Ldarg_0, (object)null);
			val.labels.AddRange(list[item].labels);
			list[item].labels.Clear();
			list.InsertRange(item, (IEnumerable<CodeInstruction>)(object)new CodeInstruction[2]
			{
				val,
				new CodeInstruction(OpCodes.Call, (object)AccessTools.Method(typeof(AirborneIceMovement), "OnSurface", (Type[])null, (Type[])null))
			});
			return list;
		}
	}
	internal static class AmmoConservationPerArrow
	{
		[HarmonyPatch(typeof(UseAmmoTranspilerPatch), "CustomRemoveItem")]
		private static class SpendAmmunition
		{
			private static bool Prefix(Inventory inventory, ItemData item, int amount, ref bool __result)
			{
				conservedAmmo = null;
				conservedLoadedArrow = false;
				Player localPlayer = Player.m_localPlayer;
				if ((Object)(object)localPlayer == (Object)null || inventory != ((Humanoid)localPlayer).GetInventory() || item == null)
				{
					return true;
				}
				float num = Mathf.Clamp01(PlayerExtensions.GetTotalActiveMagicEffectValue(localPlayer, MagicEffectType.AmmoConservation, 0.01f, (ItemData)null));
				if (num <= 0f)
				{
					return true;
				}
				if (MultiShot.IsTripleShotActive)
				{
					amount *= MultiShot.ShotProjectiles;
					MultiShot.IsTripleShotActive = false;
					MultiShot.ShotProjectiles = 0;
				}
				int num2 = Math.Min(amount, item.m_stack);
				int num3 = 0;
				conservedAmmo = item;
				for (int i = 0; i < num2; i++)
				{
					bool flag = Random.value < num;
					if (i == 0)
					{
						conservedLoadedArrow = flag;
					}
					if (!flag)
					{
						num3++;
					}
				}
				__result = num3 == 0 || inventory.RemoveItem(item, num3);
				return false;
			}
		}

		[HarmonyPatch(typeof(AmmoConservation_Attack_UseAmmo_Patch), "Postfix")]
		private static class FinishConservation
		{
			private static bool Prefix(Attack __0, bool __1, ItemData __2)
			{
				Player localPlayer = Player.m_localPlayer;
				bool flag = (Object)(object)localPlayer != (Object)null && (Object)(object)GameFields.AttackCharacter.Invoke(__0) == (Object)(object)localPlayer && __2 != null && __2 == conservedAmmo;
				if (__1 && flag && conservedLoadedArrow)
				{
					ItemData weapon = __0.GetWeapon();
					if (weapon != null && weapon.m_shared.m_attack.m_requiresReload)
					{
						SkipReload.Invoke() = true;
					}
				}
				conservedAmmo = null;
				conservedLoadedArrow = false;
				return !flag;
			}
		}

		private static ItemData conservedAmmo;

		private static bool conservedLoadedArrow;

		private static readonly FieldRef<bool> SkipReload = AccessTools.StaticFieldRefAccess<bool>(AccessTools.Field(typeof(AmmoConservation), "skipReload"));
	}
	internal static class AmmoCountBadge
	{
		private sealed class BarState
		{
			internal Player Player;

			internal ItemData Ammo;

			internal object FirstElement;

			internal int ElementCount;

			internal int InventoryVersion = -1;

			internal int ConfigurationVersion = -1;

			internal bool AfterReset;

			internal bool Conjuring;

			internal readonly List<(ItemData Item, Vector2i Position)> Items = new List<(ItemData, Vector2i)>();
		}

		private const float IconGap = 2f;

		private static readonly FieldRef<HotkeyBar, object> Elements = AccessTools.FieldRefAccess<HotkeyBar, object>("m_elements");

		private static readonly ConditionalWeakTable<HotkeyBar, BarState> Bars = new ConditionalWeakTable<HotkeyBar, BarState>();

		private static readonly Dictionary<string, string> Counts = new Dictionary<string, string>();

		private static FieldRef<bool> reset;

		private static int inventoryVersion;

		private static int configurationVersion;

		private static int countedVersion = -1;

		private static Inventory countedInventory;

		internal static void Initialize(IntegrationPatches harmony, ManualLogSource log)
		{
			//IL_0101: Unknown result type (might be due to invalid IL or missing references)
			//IL_0116: Unknown result type (might be due to invalid IL or missing references)
			//IL_012b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0137: Expected O, but got Unknown
			//IL_0137: Expected O, but got Unknown
			//IL_0137: Expected O, but got Unknown
			//IL_0149: Unknown result type (might be due to invalid IL or missing references)
			//IL_0157: Expected O, but got Unknown
			//IL_017f: Unknown result type (might be due to invalid IL or missing references)
			//IL_018c: Expected O, but got Unknown
			if (PluginIds.TryGet("aedenthorn.AmmoCount", out var plugin))
			{
				Type type = AccessTools.Inner(((object)plugin.Instance).GetType(), "HotkeyBar_Update_Patch");
				MethodInfo methodInfo = ((type == null) ? null : AccessTools.Method(type, "CheckAmmo", new Type[2]
				{
					typeof(ItemData),
					typeof(GameObject)
				}, (Type[])null));
				MethodInfo methodInfo2 = ((type == null) ? null : AccessTools.Method(type, "Postfix", new Type[2]
				{
					typeof(HotkeyBar),
					typeof(List<ItemData>)
				}, (Type[])null));
				if (methodInfo == null || !methodInfo.IsStatic || methodInfo2 == null)
				{
					throw new InvalidOperationException("Ammo Count's badge check moved.");
				}
				reset = AccessTools.StaticFieldRefAccess<bool>(AccessTools.Field(((object)plugin.Instance).GetType(), "reset"));
				harmony.Patch(methodInfo, new HarmonyMethod(typeof(AmmoCountBadge), "OnlyOnWeapons", (Type[])null), new HarmonyMethod(typeof(AmmoCountBadge), "LayOut", (Type[])null), new HarmonyMethod(typeof(AmmoCountBadge), "CountEveryStack", (Type[])null));
				harmony.Patch(methodInfo2, new HarmonyMethod(typeof(AmmoCountBadge), "RefreshNeeded", (Type[])null));
				harmony.Patch(AccessTools.Method(typeof(Inventory), "Changed", (Type[])null, (Type[])null), null, new HarmonyMethod(typeof(AmmoCountBadge), "InventoryChanged", (Type[])null));
				EventHandler<SettingChangedEventArgs> changed = delegate
				{
					configurationVersion++;
				};
				IntegrationChanges.Defer(delegate
				{
					plugin.Instance.Config.SettingChanged += changed;
				}, delegate
				{
					plugin.Instance.Config.SettingChanged -= changed;
				});
				log.LogInfo((object)"Ammo Count's badge shows only on weapons, counts every stack of the ammunition, and scales with its slot with the icon beside the count.");
			}
		}

		private static void InventoryChanged()
		{
			inventoryVersion++;
		}

		private static bool RefreshNeeded(HotkeyBar __0, List<ItemData> __1)
		{
			//IL_0130: Unknown result type (might be due to invalid IL or missing references)
			//IL_013d: Unknown result type (might be due to invalid IL or missing references)
			//IL_01d4: Unknown result type (might be due to invalid IL or missing references)
			Player localPlayer = Player.m_localPlayer;
			if (!Object.op_Implicit((Object)(object)localPlayer))
			{
				return true;
			}
			BarState value = Bars.GetValue(__0, (HotkeyBar _) => new BarState());
			IList list = (IList)Elements.Invoke(__0);
			object obj = ((list.Count == 0) ? null : list[0]);
			ItemData ammoItem = ((Humanoid)localPlayer).GetAmmoItem();
			bool flag = reset.Invoke();
			bool flag2 = PlayerExtensions.HasActiveMagicEffect(localPlayer, MagicEffectType.ConjuredArrows);
			bool flag3 = (Object)(object)value.Player != (Object)(object)localPlayer || value.Ammo != ammoItem || value.Conjuring != flag2 || value.InventoryVersion != inventoryVersion || value.ConfigurationVersion != configurationVersion || value.FirstElement != obj || value.ElementCount != list.Count || value.Items.Count != __1.Count || flag || value.AfterReset;
			if (!flag3)
			{
				for (int num = 0; num < __1.Count; num++)
				{
					if (value.Items[num].Item != __1[num] || value.Items[num].Position != __1[num].m_gridPos)
					{
						flag3 = true;
						break;
					}
				}
			}
			if (!flag3)
			{
				return false;
			}
			value.Player = localPlayer;
			value.Ammo = ammoItem;
			value.Conjuring = flag2;
			value.InventoryVersion = inventoryVersion;
			value.ConfigurationVersion = configurationVersion;
			value.FirstElement = obj;
			value.ElementCount = list.Count;
			value.AfterReset = flag;
			value.Items.Clear();
			foreach (ItemData item in __1)
			{
				value.Items.Add((item, item.m_gridPos));
			}
			return true;
		}

		private static bool OnlyOnWeapons(ItemData __0, GameObject __1)
		{
			float num = default(float);
			if (__0.IsWeapon() && !ConjuredArrows.TryGetShotCost(__0, ref num))
			{
				return true;
			}
			__1.SetActive(false);
			return false;
		}

		private static IEnumerable<CodeInstruction> CountEveryStack(IEnumerable<CodeInstruction> instructions)
		{
			FieldInfo stack = AccessTools.Field(typeof(ItemData), "m_stack");
			MethodInfo text = AccessTools.Method(typeof(int), "ToString", Type.EmptyTypes, (Type[])null);
			List<CodeInstruction> code = instructions.ToList();
			List<int> list = (from index in Enumerable.Range(0, code.Count - 1)
				where CodeInstructionExtensions.LoadsField(code[index], stack, true) && CodeInstructionExtensions.Calls(code[index + 1], text)
				select index).ToList();
			if (list.Count != 1)
			{
				throw new InvalidOperationException("Ammo Count's count changed.");
			}
			int num = list[0];
			code[num].opcode = OpCodes.Call;
			code[num].operand = AccessTools.Method(typeof(AmmoCountBadge), "Total", (Type[])null, (Type[])null);
			code[num + 1].opcode = OpCodes.Nop;
			code[num + 1].operand = null;
			return code;
		}

		private static string Total(ItemData ammo)
		{
			Inventory inventory = ((Humanoid)Player.m_localPlayer).GetInventory();
			if (countedInventory != inventory || countedVersion != inventoryVersion)
			{
				Counts.Clear();
				countedInventory = inventory;
				countedVersion = inventoryVersion;
			}
			string name = ammo.m_shared.m_name;
			if (!Counts.TryGetValue(name, out var value))
			{
				value = (Counts[name] = inventory.CountItems(name, -1, false).ToString());
			}
			return value;
		}

		private static void LayOut(GameObject __1)
		{
			//IL_000f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0014: Unknown result type (might be due to invalid IL or missing references)
			//IL_0026: Unknown result type (might be due to invalid IL or missing references)
			//IL_0068: Unknown result type (might be due to invalid IL or missing references)
			//IL_0073: Unknown result type (might be due to invalid IL or missing references)
			//IL_0078: Unknown result type (might be due to invalid IL or missing references)
			//IL_0094: Unknown result type (might be due to invalid IL or missing references)
			//IL_0099: Unknown result type (might be due to invalid IL or missing references)
			//IL_00aa: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ba: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bf: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c9: Unknown result type (might be due to invalid IL or missing references)
			if (!__1.activeSelf)
			{
				return;
			}
			if (__1.transform.localScale != Vector3.one)
			{
				__1.transform.localScale = Vector3.one;
			}
			Transform obj = __1.transform.Find("Icon");
			RectTransform val = (RectTransform)(object)((obj is RectTransform) ? obj : null);
			if (val != null)
			{
				TMP_Text component = ((Component)__1.transform.Find("Text")).GetComponent<TMP_Text>();
				RectTransform rectTransform = component.rectTransform;
				float x = rectTransform.anchoredPosition.x;
				Rect rect = rectTransform.rect;
				float num = x + ((Rect)(ref rect)).xMax - component.preferredWidth;
				float num2 = num - 2f;
				rect = val.rect;
				Vector2 val2 = default(Vector2);
				((Vector2)(ref val2))..ctor(num2 - ((Rect)(ref rect)).width / 2f, val.anchoredPosition.y);
				if (val.anchoredPosition != val2)
				{
					val.anchoredPosition = val2;
				}
			}
		}
	}
	[HarmonyPatch(typeof(SE_CustomFinder), "UpdateStatusEffect")]
	internal static class AndvaranautTracking
	{
		private const float ScanInterval = 1f;

		private static readonly FieldRef<List<Beacon>> Beacons = AccessTools.StaticFieldRefAccess<List<Beacon>>(AccessTools.Field(typeof(Beacon), "m_instances"));

		private static SE_CustomFinder finder;

		private static bool hasTarget;

		private static bool beyondSearchRadius;

		private static Vector3 targetPosition;

		private static float scanTimer;

		private static float sincePing;

		internal static string ModeLabel(StatusEffect effect)
		{
			if (!((Object)(object)effect != (Object)(object)finder) && hasTarget)
			{
				if (!beyondSearchRadius)
				{
					return "Near";
				}
				return "Far";
			}
			return null;
		}

		private static bool IsLocalRing(SE_CustomFinder ring)
		{
			if ((Object)(object)Player.m_localPlayer != (Object)null && (Object)(object)((StatusEffect)ring).m_character == (Object)(object)Player.m_localPlayer)
			{
				return ring.RequiredComponentTypes?.Contains(typeof(TreasureMapChest)) ?? false;
			}
			return false;
		}

		internal static void RefreshTarget()
		{
			//IL_0027: Unknown result type (might be due to invalid IL or missing references)
			//IL_003e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0043: Unknown result type (might be due to invalid IL or missing references)
			if (!((Object)(object)finder == (Object)null) && IsLocalRing(finder))
			{
				Player localPlayer = Player.m_localPlayer;
				FindTarget(localPlayer, ((Component)localPlayer).transform.position);
				beyondSearchRadius = hasTarget && Utils.DistanceXZ(((Component)localPlayer).transform.position, targetPosition) > Plugin.AndvaranautSearchRadius.Value;
				scanTimer = 0f;
			}
		}

		private static bool Prefix(SE_CustomFinder __instance)
		{
			return !IsLocalRing(__instance);
		}

		private static void Postfix(SE_CustomFinder __instance, float dt)
		{
			//IL_0053: Unknown result type (might be due to invalid IL or missing references)
			//IL_0058: Unknown result type (might be due to invalid IL or missing references)
			//IL_007c: Unknown result type (might be due to invalid IL or missing references)
			//IL_008a: Unknown result type (might be due to invalid IL or missing references)
			//IL_008b: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bd: Unknown result type (might be due to invalid IL or missing references)
			if (!IsLocalRing(__instance))
			{
				return;
			}
			Player localPlayer = Player.m_localPlayer;
			if ((Object)(object)__instance != (Object)(object)finder)
			{
				finder = __instance;
				hasTarget = false;
				scanTimer = 1f;
				sincePing = Plugin.AndvaranautSlowestPing.Value;
			}
			sincePing += dt;
			Vector3 position = ((Component)localPlayer).transform.position;
			scanTimer += dt;
			if (scanTimer >= 1f)
			{
				scanTimer = 0f;
				FindTarget(localPlayer, position);
			}
			if (hasTarget)
			{
				float num = Utils.DistanceXZ(position, targetPosition);
				beyondSearchRadius = num > Plugin.AndvaranautSearchRadius.Value;
				if (sincePing >= Interval(num) && (!beyondSearchRadius || IsFacing(localPlayer, position)))
				{
					Ping((SE_Finder)(object)__instance, localPlayer);
				}
			}
		}

		private static bool IsFacing(Player player, Vector3 position)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_0007: Unknown result type (might be due to invalid IL or missing references)
			//IL_000c: Unknown result type (might be due to invalid IL or missing references)
			//IL_000d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0012: Unknown result type (might be due to invalid IL or missing references)
			//IL_004c: Unknown result type (might be due to invalid IL or missing references)
			//IL_004d: Unknown result type (might be due to invalid IL or missing references)
			Vector3 lookDir = ((Character)player).GetLookDir();
			Vector3 val = targetPosition - position;
			lookDir.y = 0f;
			val.y = 0f;
			float value = Plugin.AndvaranautFacingAngle.Value;
			if (value > 0f && ((Vector3)(ref lookDir)).sqrMagnitude >= 0.0001f)
			{
				return Vector3.Angle(lookDir, val) <= value;
			}
			return false;
		}

		private static float Interval(float distance)
		{
			float value = Plugin.AndvaranautSearchRadius.Value;
			float value2 = Plugin.AndvaranautMaxRange.Value;
			float value3 = Plugin.AndvaranautSlowestPing.Value;
			float value4 = Plugin.AndvaranautSearchRadiusPing.Value;
			if (distance <= value)
			{
				return Mathf.Lerp(Plugin.AndvaranautNearestPing.Value, value4, Mathf.InverseLerp(0f, value, distance));
			}
			if (value2 <= value)
			{
				return value3;
			}
			return Mathf.Lerp(value4, value3, Mathf.InverseLerp(value, value2, distance));
		}

		private static void Ping(SE_Finder ring, Player player)
		{
			//IL_0014: Unknown result type (might be due to invalid IL or missing references)
			//IL_001a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0027: Unknown result type (might be due to invalid IL or missing references)
			Transform transform = ((Component)player).transform;
			ZNetView.m_forceDisableInit = true;
			try
			{
				ring.m_pingEffectFar.Create(transform.position, transform.rotation, transform, 1f, -1, ((Character)player).GetZDOID());
			}
			finally
			{
				ZNetView.m_forceDisableInit = false;
			}
			sincePing = 0f;
		}

		private static void FindTarget(Player player, Vector3 position)
		{
			//IL_0079: Unknown result type (might be due to invalid IL or missing references)
			//IL_007b: Unknown result type (might be due to invalid IL or missing references)
			//IL_005c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0061: Unknown result type (might be due to invalid IL or missing references)
			//IL_0068: Unknown result type (might be due to invalid IL or missing references)
			//IL_006d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0072: Unknown result type (might be due to invalid IL or missing references)
			//IL_0077: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ed: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ef: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00dc: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00eb: Unknown result type (might be due to invalid IL or missing references)
			hasTarget = false;
			float nearest = ((Plugin.AndvaranautMaxRange.Value > 0f) ? Plugin.AndvaranautMaxRange.Value : float.MaxValue);
			AdventureSaveData adventureSaveData = PlayerExtensions_Adventure.GetAdventureSaveData(player);
			long playerID = player.GetPlayerID();
			foreach (TreasureMapChestInfo unfoundTreasureChest in adventureSaveData.GetUnfoundTreasureChests())
			{
				if (!LocateChest(unfoundTreasureChest, playerID, out var located))
				{
					located = SerializableVector3.op_Implicit(unfoundTreasureChest.Position) + SerializableVector3.op_Implicit(unfoundTreasureChest.MinimapCircleOffset);
				}
				Consider(located, position, ref nearest);
			}
			foreach (BountyInfo inProgressBounty in adventureSaveData.GetInProgressBounties())
			{
				if (!inProgressBounty.Slain)
				{
					if (!LocateBounty(inProgressBounty.ID, position, out var located2))
					{
						located2 = SerializableVector3.op_Implicit(inProgressBounty.Position) + SerializableVector3.op_Implicit(inProgressBounty.MinimapCircleOffset);
					}
					Consider(located2, position, ref nearest);
				}
			}
		}

		private static void Consider(Vector3 candidate, Vector3 position, ref float nearest)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0017: Unknown result type (might be due to invalid IL or missing references)
			//IL_0018: Unknown result type (might be due to invalid IL or missing references)
			float num = Utils.DistanceXZ(position, candidate);
			if (!(num >= nearest))
			{
				nearest = num;
				hasTarget = true;
				targetPosition = candidate;
			}
		}

		private static bool LocateChest(TreasureMapChestInfo map, long playerId, out Vector3 located)
		{
			//IL_00b9: Unknown result type (might be due to invalid IL or missing references)
			//IL_0064: Unknown result type (might be due to invalid IL or missing references)
			//IL_006a: Unknown result type (might be due to invalid IL or missing references)
			//IL_008d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0092: Unknown result type (might be due to invalid IL or missing references)
			foreach (Beacon item in Beacons.Invoke())
			{
				TreasureMapChest val = (((Object)(object)item != (Object)null) ? ((Component)item).GetComponent<TreasureMapChest>() : null);
				ZNetView val2 = (((Object)(object)val != (Object)null) ? ((Component)val).GetComponent<ZNetView>() : null);
				if (!((Object)(object)val2 == (Object)null) && val2.IsValid() && val.Interval == map.Interval && val.Biome == map.Biome && val2.GetZDO().GetLong("creator", 0L) == playerId)
				{
					located = ((Component)val).transform.position;
					return true;
				}
			}
			located = default(Vector3);
			return false;
		}

		private static bool LocateBounty(string bountyId, Vector3 position, out Vector3 located)
		{
			//IL_0007: Unknown result type (might be due to invalid IL or missing references)
			//IL_007b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0082: Unknown result type (might be due to invalid IL or missing references)
			//IL_009d: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a2: Unknown result type (might be due to invalid IL or missing references)
			float num = float.MaxValue;
			located = default(Vector3);
			foreach (Character allCharacter in Character.GetAllCharacters())
			{
				ZNetView val = (((Object)(object)allCharacter != (Object)null && !allCharacter.IsDead()) ? ((Component)allCharacter).GetComponent<ZNetView>() : null);
				if (!((Object)(object)val == (Object)null) && val.IsValid() && BountyCreature.IsMainTarget(val.GetZDO()) && !(val.GetZDO().GetString(BountyCreature.IdKey, "") != bountyId))
				{
					float num2 = Utils.DistanceXZ(position, ((Component)allCharacter).transform.position);
					if (!(num2 >= num))
					{
						num = num2;
						located = ((Component)allCharacter).transform.position;
					}
				}
			}
			return num < float.MaxValue;
		}
	}
	[HarmonyPatch(typeof(StatusEffect), "GetIconText")]
	internal static class AndvaranautModeLabel
	{
		private static void Postfix(StatusEffect __instance, ref string __result)
		{
			__result = AndvaranautTracking.ModeLabel(__instance) ?? __result;
		}
	}
	internal static class ArmorDisplay
	{
		internal static void Initialize(IntegrationPatches harmony)
		{
			//IL_006c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0078: Expected O, but got Unknown
			if (PluginIds.TryGet("MK_BetterUI", out var plugin))
			{
				Type type = ((object)plugin.Instance).GetType().Assembly.GetType("BetterUI.GameClasses.BetterInventoryGui");
				MethodInfo methodInfo = ((type == null) ? null : AccessTools.Method(type, "PatchArmor", (Type[])null, (Type[])null));
				if (methodInfo == n

patchers/EpicLootProgressionFix.Preloader.dll

Decompiled 16 hours ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Threading.Tasks;
using BepInEx.Logging;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using Mono.Cecil;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: AssemblyVersion("0.0.0.0")]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)]
	internal sealed class RefSafetyRulesAttribute : Attribute
	{
		public readonly int Version;

		public RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
}
namespace EpicLootProgressionFix.Preloader
{
	public static class DiscordConnectorFileLog
	{
		private const string HarmonyId = "valheim.epiclootprogressionfix.discord-file-log";

		private static readonly object writes = new object();

		private static FieldInfo logPathField;

		private static FieldInfo loggerField;

		public static IEnumerable<string> TargetDLLs => Array.Empty<string>();

		public static void Patch(AssemblyDefinition assembly)
		{
		}

		public static void Initialize()
		{
			AppDomain.CurrentDomain.AssemblyLoad += OnAssemblyLoad;
		}

		private static void OnAssemblyLoad(object sender, AssemblyLoadEventArgs args)
		{
			//IL_012f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0145: Unknown result type (might be due to invalid IL or missing references)
			//IL_0153: Expected O, but got Unknown
			if (args.LoadedAssembly.GetName().Name != "DiscordConnector")
			{
				return;
			}
			Type? type = args.LoadedAssembly.GetType("DiscordConnector.Common.VdcLogger", throwOnError: true);
			FieldInfo fieldInfo = AccessTools.Field(type, "s_logFilePath");
			FieldInfo fieldInfo2 = AccessTools.Field(type, "s_logger");
			MethodInfo methodInfo = AccessTools.DeclaredMethod(type, "LogToFileAsync", new Type[2]
			{
				typeof(string),
				typeof(string)
			}, (Type[])null);
			if (fieldInfo?.FieldType != typeof(string) || fieldInfo2?.FieldType != typeof(ManualLogSource) || methodInfo?.ReturnType != typeof(Task))
			{
				throw new InvalidOperationException("DiscordConnector's file logger changed.");
			}
			Patches patchInfo = Harmony.GetPatchInfo((MethodBase)methodInfo);
			if (patchInfo != null)
			{
				foreach (Patch prefix in patchInfo.Prefixes)
				{
					if (prefix.owner == "valheim.epiclootprogressionfix.discord-file-log")
					{
						return;
					}
				}
			}
			logPathField = fieldInfo;
			loggerField = fieldInfo2;
			new Harmony("valheim.epiclootprogressionfix.discord-file-log").Patch((MethodBase)methodInfo, new HarmonyMethod(typeof(DiscordConnectorFileLog), "QueueWrite", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
			Logger.CreateLogSource("Progression Fix preloader").LogInfo((object)"DiscordConnector file writes are serialized before startup.");
		}

		private static bool QueueWrite(string severity, string message, ref Task __result)
		{
			//IL_001c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0028: Expected O, but got Unknown
			__result = Write((string)logPathField.GetValue(null), (ManualLogSource)loggerField.GetValue(null), severity, message);
			return false;
		}

		private static Task Write(string path, ManualLogSource log, string severity, string message)
		{
			lock (writes)
			{
				try
				{
					using StreamWriter streamWriter = new StreamWriter(path, append: true);
					streamWriter.WriteLine($"{DateTime.Now} [{severity}]: {message}");
				}
				catch (Exception ex)
				{
					log.LogError((object)("Error writing to log file: " + ex.Message));
				}
			}
			return Task.CompletedTask;
		}
	}
}