plugins/Rist/Rist.dll

Decompiled a day ago
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Text;
using BepInEx;
using BepInEx.Bootstrap;
using BepInEx.Configuration;
using BepInEx.Logging;
using Ezomic.Core;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using TMPro;
using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.Events;
using UnityEngine.Sprites;
using UnityEngine.UI;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETFramework,Version=v4.6.2", FrameworkDisplayName = ".NET Framework 4.6.2")]
[assembly: AssemblyCompany("Thijssen Software")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyCopyright("Copyright (c) 2026 Robbin Thijssen")]
[assembly: AssemblyDescription("A character level beside the skills, paid out in cards you draft and deepen.")]
[assembly: AssemblyFileVersion("1.7.0.0")]
[assembly: AssemblyInformationalVersion("1.7.0+87775339d5d1602a2e7387c6a68ddb37f4dd31b5")]
[assembly: AssemblyProduct("Rist")]
[assembly: AssemblyTitle("Rist")]
[assembly: AssemblyVersion("1.7.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 Rist
{
	internal static class AnsweringBlow
	{
		internal static class Arm
		{
			private const float EnemyRange = 8f;

			[HarmonyPatch(typeof(Player), "RPC_HitWhileDodging")]
			[HarmonyPrefix]
			private static void Before(Player __instance, out bool __state)
			{
				__state = RistConfig.Enabled.Value && __instance == Player.m_localPlayer && Bind() && !_beenHit.Invoke(__instance);
			}

			[HarmonyPatch(typeof(Player), "RPC_HitWhileDodging")]
			[HarmonyPostfix]
			private static void After(Player __instance, bool __state)
			{
				if (__state && _beenHit.Invoke(__instance) && !(Time.time <= _armedUntil) && !(Effects.TotalFor("*answer") <= 0f) && EnemyNear(__instance))
				{
					_armedUntil = Time.time + 4f;
					((Character)__instance).Message((MessageType)1, "Answering blow ready", 0, (Sprite)null, false);
				}
			}

			private static bool EnemyNear(Player player)
			{
				//IL_0006: Unknown result type (might be due to invalid IL or missing references)
				//IL_000b: Unknown result type (might be due to invalid IL or missing references)
				//IL_003f: Unknown result type (might be due to invalid IL or missing references)
				//IL_0046: Unknown result type (might be due to invalid IL or missing references)
				Vector3 position = ((Component)player).transform.position;
				foreach (Character allCharacter in Character.GetAllCharacters())
				{
					if (!((Object)(object)allCharacter == (Object)null) && !allCharacter.IsDead() && (object)allCharacter != player && BaseAI.IsEnemy((Character)(object)player, allCharacter) && Vector3.Distance(position, ((Component)allCharacter).transform.position) <= 8f)
					{
						return true;
					}
				}
				return false;
			}
		}

		internal static class Spend
		{
			[HarmonyPatch(typeof(Character), "Damage")]
			[HarmonyPrefix]
			private static void Damaging(Character __instance, HitData hit)
			{
				//IL_004a: Unknown result type (might be due to invalid IL or missing references)
				//IL_0050: Invalid comparison between Unknown and I4
				if (_armedUntil < 0f || hit == null || (Object)(object)__instance == (Object)null)
				{
					return;
				}
				if (Time.time > _armedUntil)
				{
					_armedUntil = -1f;
					return;
				}
				Player localPlayer = Player.m_localPlayer;
				if (!((Object)(object)localPlayer == (Object)null) && (object)hit.GetAttacker() == localPlayer && (int)hit.m_hitType == 2 && BaseAI.IsEnemy((Character)(object)localPlayer, __instance))
				{
					_armedUntil = -1f;
					float num = Effects.TotalFor("*answer");
					if (num > 0f)
					{
						((DamageTypes)(ref hit.m_damage)).Modify(1f + num);
					}
					float num2 = Mathf.Clamp01(Effects.TotalFor("*answer:stagger"));
					if (num2 > 0f && __instance.m_staggerWhenBlocked && !Bosses.Is(__instance) && Random.value < num2)
					{
						hit.m_staggerMultiplier = Mathf.Max(hit.m_staggerMultiplier, 100f);
					}
				}
			}
		}

		internal const string Bonus = "*answer";

		internal const string Stagger = "*answer:stagger";

		private const float Window = 4f;

		private static float _armedUntil = -1f;

		private static FieldRef<Player, bool> _beenHit;

		private static bool _bound;

		private static bool _bindFailed;

		private static bool Bind()
		{
			if (_bound)
			{
				return true;
			}
			if (_bindFailed)
			{
				return false;
			}
			try
			{
				_beenHit = AccessTools.FieldRefAccess<Player, bool>("m_beenHitWhileDodging");
				_bound = true;
			}
			catch (Exception ex)
			{
				_bindFailed = true;
				RistPlugin.Log.LogError((object)("Answering blow could not reach the game's dodge field and is off for this session: " + ex.Message));
			}
			return _bound;
		}
	}
	internal static class Bosses
	{
		internal static bool Is(Character c)
		{
			if ((Object)(object)c == (Object)null)
			{
				return false;
			}
			if (c.IsBoss())
			{
				return true;
			}
			string defeatSetGlobalKey = c.m_defeatSetGlobalKey;
			if (string.IsNullOrEmpty(defeatSetGlobalKey))
			{
				return false;
			}
			try
			{
				Enum.Parse(typeof(GlobalKeys), defeatSetGlobalKey, ignoreCase: true);
				return false;
			}
			catch
			{
				return true;
			}
		}
	}
	internal static class AttackSpeed
	{
		internal static class Reload
		{
			[HarmonyPatch(typeof(ItemData), "GetWeaponLoadingTime")]
			[HarmonyPostfix]
			private static void Loading(ItemData __instance, ref float __result)
			{
				//IL_003b: Unknown result type (might be due to invalid IL or missing references)
				//IL_0042: Invalid comparison between Unknown and I4
				if (RistConfig.Enabled.Value && !(__result <= 0f) && __instance != null && __instance.m_shared != null && __instance.m_shared.m_attack.m_requiresReload && (int)__instance.m_shared.m_skillType == 14)
				{
					float num = Mathf.Min(Effects.TotalFor("*attackspeed:ranged"), Mathf.Max(0f, RistConfig.AttackSpeedMax.Value));
					if (!(num <= 0f))
					{
						__result /= 1f + num;
					}
				}
			}
		}

		internal static class Unbroken
		{
			private static Attack _fired;

			private static FieldRef<Attack, Humanoid> _attackCharacter;

			private static FieldRef<Attack, ItemData> _attackWeapon;

			private static FieldRef<Humanoid, Attack> _currentAttack;

			private static FieldRef<Attack, int> _burstsFired;

			private static bool _bound;

			private static bool _bindFailed;

			private static bool Bind()
			{
				if (_bound)
				{
					return true;
				}
				if (_bindFailed)
				{
					return false;
				}
				try
				{
					_attackCharacter = AccessTools.FieldRefAccess<Attack, Humanoid>("m_character");
					_attackWeapon = AccessTools.FieldRefAccess<Attack, ItemData>("m_weapon");
					_currentAttack = AccessTools.FieldRefAccess<Humanoid, Attack>("m_currentAttack");
					_burstsFired = AccessTools.FieldRefAccess<Attack, int>("m_projectileBurstsFired");
					_bound = true;
				}
				catch (Exception ex)
				{
					_bindFailed = true;
					RistPlugin.Log.LogError((object)("Quick chant's capstone could not reach the game's attack fields and is off for this session: " + ex.Message));
				}
				return _bound;
			}

			[HarmonyPatch(typeof(Attack), "OnAttackTrigger")]
			[HarmonyPrefix]
			private static void Fired(Attack __instance)
			{
				if (Bind() && (object)_attackCharacter.Invoke(__instance) == Player.m_localPlayer)
				{
					_fired = __instance;
				}
			}

			[HarmonyPatch(typeof(Character), "RPC_Stagger")]
			[HarmonyPrefix]
			private static bool Staggering(Character __instance)
			{
				if (!RistConfig.Enabled.Value)
				{
					return true;
				}
				Player localPlayer = Player.m_localPlayer;
				if ((Object)(object)localPlayer == (Object)null || (object)__instance != localPlayer)
				{
					return true;
				}
				if (!((Character)localPlayer).InAttack() || !Bind())
				{
					return true;
				}
				Attack val = _currentAttack.Invoke((Humanoid)(object)localPlayer);
				if (val == null)
				{
					return true;
				}
				ItemData val2 = _attackWeapon.Invoke(val);
				if (val2 == null || val2.m_shared == null || CategoryOf(val2) != "*attackspeed:magic")
				{
					return true;
				}
				if (val == _fired && (val.m_loopingAttack || val.m_projectileBursts <= 1 || _burstsFired.Invoke(val) >= val.m_projectileBursts))
				{
					return true;
				}
				return Effects.TotalFor("*unbroken") <= 0f;
			}
		}

		internal static class BowDraw
		{
			private static int _frame = -1;

			private static float _bonus;

			[HarmonyPatch(typeof(Humanoid), "GetAttackDrawPercentage")]
			[HarmonyPostfix]
			private static void Drawn(Humanoid __instance, ref float __result)
			{
				//IL_0042: Unknown result type (might be due to invalid IL or missing references)
				//IL_0048: Invalid comparison between Unknown and I4
				if (__result <= 0f || __result >= 1f || !RistConfig.Enabled.Value || (object)__instance != Player.m_localPlayer)
				{
					return;
				}
				ItemData currentWeapon = __instance.GetCurrentWeapon();
				if (currentWeapon != null && currentWeapon.m_shared != null && (int)currentWeapon.m_shared.m_skillType == 8)
				{
					if (_frame != Time.frameCount)
					{
						_frame = Time.frameCount;
						_bonus = Mathf.Min(Effects.TotalFor("*attackspeed:ranged"), Mathf.Max(0f, RistConfig.AttackSpeedMax.Value));
					}
					if (!(_bonus <= 0f))
					{
						__result = Mathf.Clamp01(__result * (1f + _bonus));
					}
				}
			}
		}

		internal const string Melee = "*attackspeed:melee";

		internal const string Tools = "*attackspeed:tools";

		internal const string Ranged = "*attackspeed:ranged";

		internal const string Magic = "*attackspeed:magic";

		internal const string UnbrokenCast = "*unbroken";

		private static string CategoryOf(ItemData weapon)
		{
			//IL_0013: 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)
			//IL_0019: Unknown result type (might be due to invalid IL or missing references)
			//IL_001b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0059: Expected I4, but got Unknown
			//IL_0079: Unknown result type (might be due to invalid IL or missing references)
			//IL_0080: Invalid comparison between Unknown and I4
			if (weapon == null || weapon.m_shared == null)
			{
				return null;
			}
			SkillType skillType = weapon.m_shared.m_skillType;
			switch (skillType - 1)
			{
			case 7:
			case 13:
				return "*attackspeed:ranged";
			case 8:
			case 9:
				return "*attackspeed:magic";
			case 6:
			case 11:
			case 12:
				return "*attackspeed:tools";
			case 0:
			case 1:
			case 2:
			case 3:
			case 4:
			case 10:
				return "*attackspeed:melee";
			default:
				if ((int)weapon.m_shared.m_itemType != 19)
				{
					return null;
				}
				return "*attackspeed:tools";
			}
		}

		[HarmonyPatch(typeof(Attack), "Start")]
		[HarmonyPostfix]
		private static void Started(Attack __instance, bool __result, Humanoid character, CharacterAnimEvent animEvent, ItemData weapon)
		{
			//IL_0079: Unknown result type (might be due to invalid IL or missing references)
			//IL_007f: Invalid comparison between Unknown and I4
			if (!__result || !RistConfig.Enabled.Value || (Object)(object)character == (Object)null || (Object)(object)animEvent == (Object)null || (object)character != Player.m_localPlayer)
			{
				return;
			}
			string text = CategoryOf(weapon);
			if (text == null)
			{
				return;
			}
			float num = Effects.TotalFor(text);
			if (num <= 0f)
			{
				return;
			}
			num = Mathf.Min(num, Mathf.Max(0f, RistConfig.AttackSpeedMax.Value));
			if (text == "*attackspeed:magic")
			{
				if (__instance.m_loopingAttack && ((int)__instance.m_attackType != 2 || !__instance.m_perBurstResourceUsage))
				{
					return;
				}
				if (__instance.m_projectileBursts > 1)
				{
					__instance.m_burstInterval /= 1f + num;
				}
			}
			animEvent.Speed(1f + num);
		}
	}
	internal sealed class Card
	{
		internal string Id;

		internal string Name;

		internal string Flavour;

		internal string Effect;

		internal float PerRank;

		internal string BonusEffect = "";

		internal float BonusPerRank;

		internal string Sigil = "";

		internal string Aett = "";

		internal FieldInfo Field;

		internal FieldInfo BonusField;

		private static readonly HashSet<string> ShownAsReduction = new HashSet<string> { "*windcone" };

		private static HashSet<string> _oneBased;

		private static readonly HashSet<string> Fallback = new HashSet<string> { "m_healthRegenMultiplier", "m_staminaRegenMultiplier", "m_eitrRegenMultiplier", "m_damageModifier" };

		private static readonly HashSet<string> DeadFields = new HashSet<string> { "m_runStaminaUseModifier" };

		private static readonly HashSet<string> LowerIsBetter = new HashSet<string>
		{
			"m_runStaminaDrainModifier", "m_attackStaminaUseModifier", "m_blockStaminaUseModifier", "m_swimStaminaUseModifier", "m_jumpStaminaUseModifier", "m_sneakStaminaUseModifier", "m_dodgeStaminaUseModifier", "*stamina:move", "*stamina:fight", "m_fallDamageModifier",
			"m_stealthModifier", "m_noiseModifier", "m_staggerModifier", "*power:cooldown", "*overloaded"
		};

		private static readonly Dictionary<string, string> Labels = new Dictionary<string, string>
		{
			{ "m_addMaxCarryWeight", "carry weight" },
			{ "m_addArmor", "armour" },
			{ "m_armorMultiplier", "armour" },
			{ "*inventoryrow", "inventory row" },
			{ "*exploreradius", "map sight" },
			{ "*windcone", "dead zone" },
			{ "*rowspeed", "rowing speed" },
			{ "m_runStaminaUseModifier", "run stamina" },
			{ "m_runStaminaDrainModifier", "run stamina drain" },
			{ "m_attackStaminaUseModifier", "attack stamina" },
			{ "m_blockStaminaUseModifier", "block stamina" },
			{ "m_swimStaminaUseModifier", "swim stamina" },
			{ "m_jumpStaminaUseModifier", "jump stamina" },
			{ "m_sneakStaminaUseModifier", "sneak stamina" },
			{ "m_staminaRegenMultiplier", "stamina regen" },
			{ "m_healthRegenMultiplier", "health regen" },
			{ "m_eitrRegenMultiplier", "eitr regen" },
			{ "m_fallDamageModifier", "fall damage" },
			{ "m_stealthModifier", "detection" },
			{ "m_noiseModifier", "noise" },
			{ "m_staggerModifier", "stagger taken" },
			{ "m_raiseSkillModifier", "skill gain" },
			{ "m_skillLevelModifier", "skill levels" },
			{ "m_speedModifier", "movement speed" },
			{ "m_damageModifier", "damage" },
			{ "m_dodgeStaminaUseModifier", "dodge stamina" },
			{ "m_swimSpeedModifier", "swim speed" },
			{ "m_timedBlockBonus", "parry bonus" },
			{ "*stamina:move", "move stamina" },
			{ "*stamina:fight", "combat stamina" },
			{ "*attackspeed:melee", "melee speed" },
			{ "*attackspeed:tools", "tool speed" },
			{ "*attackspeed:ranged", "draw and reload" },
			{ "*attackspeed:magic", "cast speed" },
			{ "*unbroken", "a hit cannot break a cast" },
			{ "*damage:ranged", "bow and crossbow damage" },
			{ "*answer", "answering blow" },
			{ "*answer:stagger", "chance the answering blow staggers" },
			{ "*lowdraw", "s unseen draw" },
			{ "*lowdraw:silent", "arrows from a crouch land silent" },
			{ "*sneakattack", "sneak attack" },
			{ "*sneakattack:stagger", "an ambush staggers" },
			{ "*mead:duration", "mead duration" },
			{ "*mead:fullcask", "chance a mead is not used up" },
			{ "*power:cooldown", "power cooldown" },
			{ "*power:duration", "s of forsaken power" },
			{ "*staminadelay", "s less before stamina returns" },
			{ "*jumpheight", "jump height" },
			{ "*overloaded", "stamina walking overloaded" }
		};

		private static readonly HashSet<string> Percent = new HashSet<string>
		{
			"m_runStaminaUseModifier", "m_attackStaminaUseModifier", "m_blockStaminaUseModifier", "m_swimStaminaUseModifier", "m_jumpStaminaUseModifier", "m_sneakStaminaUseModifier", "m_staminaRegenMultiplier", "m_healthRegenMultiplier", "m_eitrRegenMultiplier", "m_fallDamageModifier",
			"m_stealthModifier", "m_noiseModifier", "m_staggerModifier", "m_raiseSkillModifier", "m_speedModifier", "m_damageModifier", "m_dodgeStaminaUseModifier", "m_swimSpeedModifier", "m_timedBlockBonus", "m_armorMultiplier",
			"*attackspeed:melee", "*attackspeed:tools", "*attackspeed:ranged", "*attackspeed:magic", "*damage:ranged", "*answer", "*answer:stagger", "*sneakattack", "*mead:duration", "*mead:fullcask",
			"*power:cooldown", "*stamina:move", "*stamina:fight", "m_runStaminaDrainModifier", "*exploreradius", "*windcone", "*rowspeed", "*jumpheight", "*overloaded"
		};

		internal static readonly HashSet<string> Specials = new HashSet<string>
		{
			"*inventoryrow", "*attackspeed:melee", "*attackspeed:tools", "*attackspeed:ranged", "*attackspeed:magic", "*unbroken", "*damage:ranged", "*answer", "*answer:stagger", "*lowdraw",
			"*lowdraw:silent", "*sneakattack", "*sneakattack:stagger", "*mead:duration", "*mead:fullcask", "*power:cooldown", "*power:duration", "*exploreradius", "*windcone", "*rowspeed",
			"*staminadelay", "*jumpheight", "*overloaded", "*stamina:move", "*stamina:fight"
		};

		private static readonly HashSet<string> Unlocks = new HashSet<string> { "*unbroken", "*lowdraw:silent", "*sneakattack:stagger" };

		private static readonly HashSet<string> Seconds = new HashSet<string> { "*lowdraw", "*power:duration", "*staminadelay" };

		internal bool IsSpecial => IsSpecialEffect(Effect);

		internal bool HasBonus => BonusEffect.Length > 0;

		internal static bool IsSpecialEffect(string effect)
		{
			if (effect != null && effect.Length > 0)
			{
				return effect[0] == '*';
			}
			return false;
		}

		internal static int BonusTimes(int rank)
		{
			int num = Mathf.Max(1, RistConfig.BonusEvery.Value);
			return rank / num;
		}

		internal string Describe(int rank)
		{
			return Format(Effect, PerRank * (float)Mathf.Max(1, rank));
		}

		internal string DescribeBonus(int times)
		{
			return Format(BonusEffect, BonusPerRank * (float)Mathf.Max(1, times));
		}

		private static string Format(string effect, float total)
		{
			if (!Labels.TryGetValue(effect, out var value))
			{
				value = effect;
			}
			if (Unlocks.Contains(effect))
			{
				return value;
			}
			if (ShownAsReduction.Contains(effect))
			{
				total = 0f - total;
			}
			if (Percent.Contains(effect))
			{
				float num = total * 100f;
				return ((num >= 0f) ? "+" : "−") + Mathf.Abs(num).ToString("0.#", CultureInfo.InvariantCulture) + "% " + value;
			}
			return ((total >= 0f) ? "+" : "−") + Mathf.Abs(total).ToString("0.#", CultureInfo.InvariantCulture) + " " + value;
		}

		internal static bool IsOneBased(string effect)
		{
			return Neutrals().Contains(effect);
		}

		private static HashSet<string> Neutrals()
		{
			if (_oneBased != null)
			{
				return _oneBased;
			}
			try
			{
				SE_Stats val = ScriptableObject.CreateInstance<SE_Stats>();
				if ((Object)(object)val == (Object)null)
				{
					_oneBased = Fallback;
					return _oneBased;
				}
				HashSet<string> hashSet = new HashSet<string>();
				FieldInfo[] fields = typeof(SE_Stats).GetFields(BindingFlags.Instance | BindingFlags.Public);
				foreach (FieldInfo fieldInfo in fields)
				{
					if (!(fieldInfo.FieldType != typeof(float)) && Mathf.Approximately((float)fieldInfo.GetValue(val), 1f))
					{
						hashSet.Add(fieldInfo.Name);
					}
				}
				Object.Destroy((Object)(object)val);
				foreach (string item in Fallback)
				{
					if (!hashSet.Contains(item))
					{
						RistPlugin.Log.LogWarning((object)("SE_Stats." + item + " no longer defaults to 1, so Rist has stopped treating it as a multiplier. Check the cards that target it - the game has changed what neutral means."));
					}
				}
				foreach (string item2 in hashSet)
				{
					if (!Fallback.Contains(item2))
					{
						RistPlugin.Log.LogInfo((object)("SE_Stats." + item2 + " defaults to 1 and is being treated as a multiplier; it was not one when Rist was written."));
					}
				}
				_oneBased = hashSet;
			}
			catch (Exception ex)
			{
				RistPlugin.Log.LogWarning((object)("Could not read SE_Stats' neutral values, so Rist is using the four multipliers it was written against. " + ex.Message));
				_oneBased = Fallback;
			}
			return _oneBased;
		}

		internal static bool HasLabel(string effect)
		{
			if (!string.IsNullOrEmpty(effect))
			{
				return Labels.ContainsKey(effect);
			}
			return false;
		}

		internal static bool ReadsAsRawFraction(string effect, float value)
		{
			if (string.IsNullOrEmpty(effect) || Percent.Contains(effect) || Seconds.Contains(effect))
			{
				return false;
			}
			if (Mathf.Abs(value) >= 1f)
			{
				return false;
			}
			return !Mathf.Approximately(value, Mathf.Round(value));
		}

		internal static bool IsDeadField(string effect)
		{
			if (!string.IsNullOrEmpty(effect))
			{
				return DeadFields.Contains(effect);
			}
			return false;
		}

		internal static bool PointsAtDrawback(string effect, float value)
		{
			if (value > 0f && !string.IsNullOrEmpty(effect))
			{
				return LowerIsBetter.Contains(effect);
			}
			return false;
		}
	}
	internal sealed class Aett
	{
		internal const int Capacity = 8;

		internal string Name;

		internal readonly List<int> Indices = new List<int>();
	}
	internal static class Cards
	{
		private static readonly List<Card> _all = new List<Card>();

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

		private static readonly List<Aett> _aetts = new List<Aett>();

		internal static IReadOnlyList<Card> All => _all;

		internal static IReadOnlyList<Aett> Aetts => _aetts;

		internal static bool Unavailable { get; private set; }

		internal static int Count => _all.Count;

		internal static Card Get(string id)
		{
			if (id == null || !_byId.TryGetValue(id, out var value))
			{
				return null;
			}
			return value;
		}

		internal static void Load()
		{
			_all.Clear();
			_byId.Clear();
			_aetts.Clear();
			Unavailable = false;
			string text = Path.Combine(Path.GetDirectoryName(typeof(Cards).Assembly.Location) ?? ".", "cards.txt");
			if (!File.Exists(text))
			{
				Unavailable = true;
				RistPlugin.Log.LogError((object)("cards.txt not found beside the DLL at " + text + " - no cards can be taken, and no card history will be reconciled. Reinstall the mod: the catalogue ships with it and is not optional."));
				return;
			}
			string contents;
			string[] array;
			try
			{
				contents = File.ReadAllText(text);
				array = File.ReadAllLines(text);
			}
			catch (Exception ex)
			{
				Unavailable = true;
				RistPlugin.Log.LogError((object)("cards.txt at " + text + " could not be read (" + ex.Message + ") - no cards can be taken, and no card history will be reconciled."));
				return;
			}
			if (RistPlugin.CorePresent)
			{
				DeclareCatalogue(contents);
			}
			int num = 0;
			string aett = "";
			string[] array2 = array;
			foreach (string obj in array2)
			{
				num++;
				string text2 = obj.Trim();
				if (text2.Length == 0 || text2[0] == '#')
				{
					continue;
				}
				if (text2.StartsWith("==", StringComparison.Ordinal))
				{
					aett = text2.Substring(2).Trim();
					continue;
				}
				string[] array3 = text2.Split(new char[1] { '|' });
				if (array3.Length < 5)
				{
					RistPlugin.Log.LogWarning((object)("cards.txt line " + num + ": expected 5 fields, got " + array3.Length + " - skipped."));
					continue;
				}
				Card card = new Card
				{
					Id = array3[0].Trim(),
					Name = array3[1].Trim(),
					Flavour = array3[2].Trim(),
					Effect = array3[3].Trim(),
					Aett = aett
				};
				if (!float.TryParse(array3[4].Trim(), NumberStyles.Float, CultureInfo.InvariantCulture, out card.PerRank))
				{
					RistPlugin.Log.LogWarning((object)("cards.txt line " + num + ": '" + array3[4].Trim() + "' is not a number - skipped."));
				}
				else if (card.Id.Length == 0)
				{
					RistPlugin.Log.LogWarning((object)("cards.txt line " + num + ": blank id - skipped."));
				}
				else if (_byId.ContainsKey(card.Id))
				{
					RistPlugin.Log.LogWarning((object)("cards.txt line " + num + ": duplicate id '" + card.Id + "' - skipped."));
				}
				else
				{
					if (!Resolve(card.Effect, num, card.Id, out card.Field))
					{
						continue;
					}
					if (array3.Length >= 8)
					{
						card.Sigil = array3[7].Trim();
					}
					if (array3.Length >= 7)
					{
						card.BonusEffect = array3[5].Trim();
						string text3 = array3[6].Trim();
						if (card.BonusEffect.Length > 0)
						{
							if (!float.TryParse(text3, NumberStyles.Float, CultureInfo.InvariantCulture, out card.BonusPerRank))
							{
								RistPlugin.Log.LogWarning((object)("cards.txt line " + num + ": bonus value '" + text3 + "' is not a number - bonus dropped."));
								card.BonusEffect = "";
							}
							else if (!Resolve(card.BonusEffect, num, card.Id, out card.BonusField))
							{
								card.BonusEffect = "";
							}
						}
					}
					_all.Add(card);
					_byId[card.Id] = card;
				}
			}
			if (_all.Count == 0)
			{
				Unavailable = true;
				RistPlugin.Log.LogError((object)"cards.txt parsed to zero usable cards. Every line was blank, commented out or rejected - see the warnings above. No card can be taken, and no card history will be reconciled.");
			}
			else
			{
				RistPlugin.Log.LogInfo((object)("Loaded " + _all.Count + " cards from cards.txt."));
				BuildAetts();
				WarnAboutMissingLabels();
			}
		}

		private static void BuildAetts()
		{
			Aett aett = null;
			List<string> list = new List<string>();
			List<string> list2 = new List<string>();
			HashSet<string> hashSet = new HashSet<string>();
			int num = 0;
			for (int i = 0; i < _all.Count; i++)
			{
				string text = _all[i].Aett ?? "";
				if (text.Length == 0)
				{
					num++;
				}
				if (aett == null || aett.Name != text || aett.Indices.Count >= 8)
				{
					int num2;
					if (aett != null)
					{
						num2 = ((aett.Name == text) ? 1 : 0);
						if (num2 != 0 && text.Length > 0 && !list.Contains(text))
						{
							list.Add(text);
						}
					}
					else
					{
						num2 = 0;
					}
					if (num2 == 0 && text.Length > 0 && hashSet.Contains(text) && !list2.Contains(text))
					{
						list2.Add(text);
					}
					if (aett != null)
					{
						hashSet.Add(aett.Name);
					}
					aett = new Aett
					{
						Name = text
					};
					_aetts.Add(aett);
				}
				aett.Indices.Add(i);
			}
			if (list.Count > 0)
			{
				RistPlugin.Log.LogWarning((object)("These ættir hold more than " + 8 + " rists, so the panel stands each overflow as a second tower of the same name: " + string.Join(", ", list.ToArray()) + ". Split the heading in cards.txt."));
			}
			if (list2.Count > 0)
			{
				RistPlugin.Log.LogWarning((object)("These ætt headings appear more than once in cards.txt, so each stands as more than one tower: " + string.Join(", ", list2.ToArray()) + ". Move the cards under a single heading."));
			}
			if (num > 0)
			{
				RistPlugin.Log.LogWarning((object)(num + " rists in cards.txt stand under no \"== Name\" heading, so the panel shows them in towers with no name. Add a heading above them."));
			}
		}

		private static void WarnAboutMissingLabels()
		{
			List<string> list = new List<string>();
			List<string> list2 = new List<string>();
			List<string> list3 = new List<string>();
			List<string> list4 = new List<string>();
			foreach (Card item in _all)
			{
				if (Card.IsDeadField(item.Effect))
				{
					list3.Add(item.Id + " (" + item.Effect + ")");
				}
				if (Card.IsDeadField(item.BonusEffect))
				{
					list3.Add(item.Id + " capstone (" + item.BonusEffect + ")");
				}
				if (Card.PointsAtDrawback(item.Effect, item.PerRank))
				{
					list4.Add(item.Id + " (" + item.Effect + " " + item.PerRank + ")");
				}
				if (Card.PointsAtDrawback(item.BonusEffect, item.BonusPerRank))
				{
					list4.Add(item.Id + " capstone (" + item.BonusEffect + " " + item.BonusPerRank + ")");
				}
				if (!string.IsNullOrEmpty(item.Effect) && !Card.HasLabel(item.Effect))
				{
					list.Add(item.Id + " (" + item.Effect + ")");
				}
				if (!string.IsNullOrEmpty(item.BonusEffect) && !Card.HasLabel(item.BonusEffect))
				{
					list.Add(item.Id + " capstone (" + item.BonusEffect + ")");
				}
				if (Card.ReadsAsRawFraction(item.Effect, item.PerRank))
				{
					list2.Add(item.Id + " (" + item.Effect + " " + item.PerRank + ")");
				}
				if (Card.ReadsAsRawFraction(item.BonusEffect, item.BonusPerRank))
				{
					list2.Add(item.Id + " capstone (" + item.BonusEffect + " " + item.BonusPerRank + ")");
				}
			}
			if (list.Count > 0)
			{
				RistPlugin.Log.LogWarning((object)("These card effects have no readable name, so the panel shows the game's own field name to the player: " + string.Join(", ", list.ToArray()) + ". Add them to Cards.Labels."));
			}
			if (list2.Count > 0)
			{
				RistPlugin.Log.LogWarning((object)("These card values are fractions but will be shown as raw numbers rather than percentages, so 0.05 reads as \"+0.1\": " + string.Join(", ", list2.ToArray()) + ". Add them to Cards.Percent."));
			}
			if (list3.Count > 0)
			{
				RistPlugin.Log.LogWarning((object)("These card effects name a field the game never reads, so they do nothing whatever the tile says: " + string.Join(", ", list3.ToArray()) + ". See Card.DeadFields for what to use instead."));
			}
			if (list4.Count > 0)
			{
				RistPlugin.Log.LogWarning((object)("These card values point the wrong way - on these fields negative is the benefit, so a positive value is a drawback: " + string.Join(", ", list4.ToArray()) + ". Flip the sign."));
			}
		}

		private static bool Resolve(string effect, int lineNo, string id, out FieldInfo field)
		{
			field = null;
			if (Card.IsSpecialEffect(effect))
			{
				if (Card.Specials.Contains(effect))
				{
					return true;
				}
				RistPlugin.Log.LogWarning((object)("cards.txt line " + lineNo + ": unknown special '" + effect + "' - card '" + id + "' skipped."));
				return false;
			}
			field = typeof(SE_Stats).GetField(effect, BindingFlags.Instance | BindingFlags.Public);
			if (field != null && field.FieldType == typeof(float))
			{
				return true;
			}
			RistPlugin.Log.LogWarning((object)("cards.txt line " + lineNo + ": SE_Stats has no public float field '" + effect + "' - card '" + id + "' skipped."));
			field = null;
			return false;
		}

		[MethodImpl(MethodImplOptions.NoInlining)]
		private static void DeclareCatalogue(string contents)
		{
			Suite.Data(contents, (string)null);
		}
	}
	[HarmonyPatch(typeof(Skills), "OnDeath")]
	internal static class DeathPenalty
	{
		private static bool Prefix()
		{
			return !RistConfig.RemoveDeathSkillLoss.Value;
		}
	}
	internal static class DeepDraught
	{
		internal static class Keep
		{
			[HarmonyPatch(typeof(Inventory), "RemoveOneItem")]
			[HarmonyPrefix]
			private static bool Removing(ItemData item)
			{
				if (_keep != null)
				{
					return item != _keep;
				}
				return true;
			}
		}

		internal const string Duration = "*mead:duration";

		internal const string FullCask = "*mead:fullcask";

		private static ItemData _keep;

		private static bool IsBuffMead(ItemData item)
		{
			//IL_0027: Unknown result type (might be due to invalid IL or missing references)
			//IL_002d: Invalid comparison between Unknown and I4
			if (item == null || item.m_shared == null)
			{
				return false;
			}
			if (item.m_shared.m_food > 0f)
			{
				return false;
			}
			if ((int)item.m_shared.m_itemType != 2)
			{
				return false;
			}
			StatusEffect consumeStatusEffect = item.m_shared.m_consumeStatusEffect;
			SE_Stats val = (SE_Stats)(object)((consumeStatusEffect is SE_Stats) ? consumeStatusEffect : null);
			if ((Object)(object)val == (Object)null || ((object)val).GetType() != typeof(SE_Stats) || ((StatusEffect)val).m_ttl <= 0f)
			{
				return false;
			}
			if (val.m_healthUpFront != 0f || val.m_healthOverTime != 0f || val.m_healthPerTick != 0f)
			{
				return false;
			}
			if (val.m_staminaUpFront != 0f || val.m_staminaOverTime != 0f)
			{
				return false;
			}
			if (val.m_eitrUpFront != 0f || val.m_eitrOverTime != 0f)
			{
				return false;
			}
			return true;
		}

		[HarmonyPatch(typeof(Player), "ConsumeItem")]
		[HarmonyPrefix]
		private static void Before(Player __instance, ItemData item)
		{
			_keep = null;
			if (RistConfig.Enabled.Value && __instance == Player.m_localPlayer)
			{
				float num = Mathf.Clamp01(Effects.TotalFor("*mead:fullcask"));
				if (!(num <= 0f) && IsBuffMead(item) && Random.value < num)
				{
					_keep = item;
				}
			}
		}

		[HarmonyPatch(typeof(Player), "ConsumeItem")]
		[HarmonyPostfix]
		private static void After(Player __instance, ItemData item, bool __result)
		{
			bool flag = _keep != null;
			_keep = null;
			if (!__result || !RistConfig.Enabled.Value || __instance != Player.m_localPlayer || !IsBuffMead(item))
			{
				return;
			}
			float num = Effects.TotalFor("*mead:duration");
			if (num > 0f)
			{
				StatusEffect statusEffect = ((Character)__instance).GetSEMan().GetStatusEffect(item.m_shared.m_consumeStatusEffect.NameHash());
				if ((Object)(object)statusEffect != (Object)null)
				{
					statusEffect.m_ttl *= 1f + num;
				}
			}
			if (flag)
			{
				((Character)__instance).Message((MessageType)1, "The cask is still full", 0, (Sprite)null, false);
			}
		}

		[HarmonyPatch(typeof(Player), "ConsumeItem")]
		[HarmonyFinalizer]
		private static void Cleanup()
		{
			_keep = null;
		}
	}
	internal static class DevConsole
	{
		[HarmonyPatch(typeof(Terminal), "InitTerminal")]
		internal static class Hook
		{
			private static void Postfix()
			{
				Register();
			}
		}

		[CompilerGenerated]
		private static class <>O
		{
			public static ConsoleEvent <0>__OnCommand;
		}

		private static bool _registered;

		private static readonly string[][] AddsToZero = new string[12][]
		{
			new string[2] { "m_runStaminaDrainModifier", "run stamina" },
			new string[2] { "m_jumpStaminaUseModifier", "jump stamina" },
			new string[2] { "m_dodgeStaminaUseModifier", "dodge stamina" },
			new string[2] { "m_swimStaminaUseModifier", "swim stamina" },
			new string[2] { "m_sneakStaminaUseModifier", "sneak stamina" },
			new string[2] { "m_attackStaminaUseModifier", "attack stamina" },
			new string[2] { "m_blockStaminaUseModifier", "block stamina" },
			new string[2] { "m_homeItemStaminaUseModifier", "tool stamina" },
			new string[2] { "m_fallDamageModifier", "fall damage" },
			new string[2] { "m_stealthModifier", "detection" },
			new string[2] { "m_noiseModifier", "noise" },
			new string[2] { "m_staggerModifier", "stagger taken" }
		};

		private static void Register()
		{
			//IL_003d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0028: Unknown result type (might be due to invalid IL or missing references)
			//IL_002d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0033: Expected O, but got Unknown
			if (!_registered)
			{
				_registered = true;
				object obj = <>O.<0>__OnCommand;
				if (obj == null)
				{
					ConsoleEvent val = OnCommand;
					<>O.<0>__OnCommand = val;
					obj = (object)val;
				}
				new ConsoleCommand("rist", "rist show | rist rank <card> <n> - this character's standing, and forcing a rank for a test", (ConsoleEvent)obj, true, false, false, false, false, false, (ConsoleOptionsFetcher)null, false, false, false);
				RistPlugin.Log.LogInfo((object)"Console command 'rist' registered (needs devcommands, host or singleplayer).");
			}
		}

		private static void OnCommand(ConsoleEventArgs args)
		{
			Terminal context = args.Context;
			if (!((Object)(object)context == (Object)null))
			{
				switch ((args.Length > 1) ? args[1].ToLowerInvariant() : "")
				{
				case "show":
					Show(context);
					return;
				case "rank":
					Rank(context, args);
					return;
				case "powers":
					Powers(context);
					return;
				}
				context.AddString("rist show            - level, xp, ranks and the armour the game is using");
				context.AddString("rist rank <card> <n> - force a card to exactly that rank");
				context.AddString("rist powers          - each forsaken power against every stone carved, and what reaches zero");
				context.AddString("card ids are the first field of cards.txt: thickhide, steadyfoot, longstride...");
			}
		}

		private static void Powers(Terminal term)
		{
			if ((Object)(object)ObjectDB.instance == (Object)null || ObjectDB.instance.m_StatusEffects == null || ObjectDB.instance.m_StatusEffects.Count == 0)
			{
				Say(term, "rist powers: no ObjectDB yet - load a world first.");
				return;
			}
			Dictionary<string, int> dictionary = new Dictionary<string, int>();
			foreach (Card item in Cards.All)
			{
				if (item != null && !string.IsNullOrEmpty(item.Id))
				{
					dictionary[item.Id] = RistConfig.MaxRank.Value;
				}
			}
			Dictionary<string, float> dictionary2 = Effects.TotalsFor(dictionary);
			float value = RistConfig.MinRunStaminaCost.Value;
			StringBuilder stringBuilder = new StringBuilder("rist powers: every stone at rank " + RistConfig.MaxRank.Value + " adds");
			string[][] addsToZero = AddsToZero;
			foreach (string[] array in addsToZero)
			{
				dictionary2.TryGetValue(array[0], out var value2);
				if (value2 != 0f)
				{
					stringBuilder.Append("  " + array[1] + " " + Signed(value2));
				}
			}
			Say(term, stringBuilder.ToString());
			Dictionary<string, float> dictionary3 = new Dictionary<string, float>();
			int num = 0;
			int num2 = 0;
			foreach (StatusEffect statusEffect in ObjectDB.instance.m_StatusEffects)
			{
				if ((Object)(object)statusEffect == (Object)null || ((Object)statusEffect).name == null || !((Object)statusEffect).name.StartsWith("GP_", StringComparison.Ordinal))
				{
					continue;
				}
				num++;
				SE_Stats val = (SE_Stats)(object)((statusEffect is SE_Stats) ? statusEffect : null);
				if (val == null)
				{
					Say(term, "  " + ((Object)statusEffect).name + " (" + ((object)statusEffect).GetType().Name + "): no stat modifiers, nothing to add up");
					continue;
				}
				Say(term, "  " + ((Object)statusEffect).name + ": " + Describe(val));
				addsToZero = AddsToZero;
				foreach (string[] array2 in addsToZero)
				{
					float num3 = Read(val, array2[0]);
					if (num3 != 0f)
					{
						dictionary3.TryGetValue(array2[0], out var value3);
						dictionary3[array2[0]] = value3 + num3;
						dictionary2.TryGetValue(array2[0], out var value4);
						num2 += Judge(term, "    ", array2, num3, value4, value);
					}
				}
			}
			if (num == 0)
			{
				Say(term, "rist powers: no GP_ status effects in ObjectDB - is this the stub ObjectDB?");
				return;
			}
			if (dictionary3.Count > 0)
			{
				Say(term, "  every power at once, as when several players cast on you:");
				addsToZero = AddsToZero;
				foreach (string[] array3 in addsToZero)
				{
					if (dictionary3.TryGetValue(array3[0], out var value5))
					{
						dictionary2.TryGetValue(array3[0], out var value6);
						num2 += Judge(term, "    ", array3, value5, value6, value);
					}
				}
			}
			Say(term, (num2 == 0) ? ("rist powers: " + num + " powers read, nothing reaches zero") : ("rist powers: " + num + " powers read, " + num2 + " REACH ZERO"));
		}

		private static int Judge(Terminal term, string indent, string[] pair, float power, float rist, float floor)
		{
			float num = power + rist;
			string text = indent + pair[1] + ": power " + Signed(power) + ", rist " + Signed(rist) + ", together " + Signed(num);
			if (pair[0] == "m_runStaminaDrainModifier" && floor > 0f && num <= -1f + floor)
			{
				Say(term, text + " - floored at x" + floor.ToString("0.00", CultureInfo.InvariantCulture) + " by MinRunStaminaCost");
				return 0;
			}
			if (power <= -1f)
			{
				Say(term, text + " - the power alone is already zero, as in vanilla");
				return 0;
			}
			if (rist > -1f && num <= -1f)
			{
				Say(term, text + " - REACHES ZERO");
				return 1;
			}
			Say(term, text);
			return 0;
		}

		private unsafe static string Describe(SE_Stats stats)
		{
			//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_012b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0130: Unknown result type (might be due to invalid IL or missing references)
			//IL_0133: Unknown result type (might be due to invalid IL or missing references)
			//IL_0135: Unknown result type (might be due to invalid IL or missing references)
			//IL_013a: Unknown result type (might be due to invalid IL or missing references)
			//IL_014e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0150: Unknown result type (might be due to invalid IL or missing references)
			//IL_0155: Unknown result type (might be due to invalid IL or missing references)
			List<string> list = new List<string>();
			FieldInfo[] fields = typeof(SE_Stats).GetFields(BindingFlags.Instance | BindingFlags.Public);
			foreach (FieldInfo fieldInfo in fields)
			{
				if (fieldInfo.FieldType != typeof(float))
				{
					continue;
				}
				float num = (float)fieldInfo.GetValue(stats);
				float num2 = (Card.IsOneBased(fieldInfo.Name) ? 1f : 0f);
				if (!(Math.Abs(num - num2) < 0.0001f))
				{
					string text = fieldInfo.Name + " " + num.ToString("0.###", CultureInfo.InvariantCulture);
					if (fieldInfo.Name == "m_damageModifier")
					{
						text = text + " on " + ((object)Unsafe.As<SkillType, SkillType>(ref stats.m_modifyAttackSkill)/*cast due to .constrained prefix*/).ToString();
					}
					list.Add(text);
				}
			}
			if (stats.m_jumpModifier != Vector3.zero)
			{
				list.Add("m_jumpModifier " + ((Vector3)(ref stats.m_jumpModifier)).ToString("0.###"));
			}
			if (stats.m_mods != null)
			{
				foreach (DamageModPair mod in stats.m_mods)
				{
					DamageType type = mod.m_type;
					string? text2 = ((object)(*(DamageType*)(&type))/*cast due to .constrained prefix*/).ToString();
					DamageModifier modifier = mod.m_modifier;
					list.Add(text2 + " " + ((object)(*(DamageModifier*)(&modifier))/*cast due to .constrained prefix*/).ToString());
				}
			}
			if (list.Count != 0)
			{
				return string.Join(", ", list.ToArray());
			}
			return "(no stat changes)";
		}

		private static float Read(SE_Stats stats, string name)
		{
			FieldInfo field = typeof(SE_Stats).GetField(name, BindingFlags.Instance | BindingFlags.Public);
			if (!(field != null) || !(field.FieldType == typeof(float)))
			{
				return 0f;
			}
			return (float)field.GetValue(stats);
		}

		private static string Signed(float value)
		{
			return value.ToString("+0.00;-0.00;0.00", CultureInfo.InvariantCulture);
		}

		private static void Say(Terminal term, string line)
		{
			term.AddString(line);
			RistPlugin.Log.LogInfo((object)line);
		}

		private static void Show(Terminal term)
		{
			Player localPlayer = Player.m_localPlayer;
			if ((Object)(object)localPlayer == (Object)null)
			{
				term.AddString("rist: no player.");
				return;
			}
			term.AddString("level " + ClientState.Level + ", xp " + ClientState.Xp.ToString("0.#", CultureInfo.InvariantCulture) + ", owed " + ClientState.Owed + (Net.IsServer ? "" : " (client - the server holds the record)"));
			bool flag = false;
			foreach (KeyValuePair<string, int> rank in ClientState.Ranks)
			{
				if (rank.Value > 0)
				{
					flag = true;
					Card card = Cards.Get(rank.Key);
					term.AddString("  " + rank.Key + " rank " + rank.Value + ((card == null) ? " (no such card)" : (" - " + card.Name)));
				}
			}
			if (!flag)
			{
				term.AddString("  no ranks");
			}
			term.AddString(Armour(localPlayer));
			term.AddString(Moving(localPlayer));
		}

		private static string Moving(Player player)
		{
			//IL_0018: Unknown result type (might be due to invalid IL or missing references)
			SEMan sEMan = ((Character)player).GetSEMan();
			float num = 1f;
			if (sEMan != null)
			{
				sEMan.ModifyRunStaminaDrain(1f, ref num, Vector3.zero, false);
			}
			float num2 = Sinews.VanillaJump(player);
			float num3 = ((num2 > 0f) ? (((Character)player).m_jumpForce / num2) : 1f);
			float num4 = num3 * num3;
			float num5 = Sinews.VanillaDelay(player) - player.m_staminaRegenDelay;
			float num6 = 1f;
			if (sEMan != null)
			{
				sEMan.ModifyStagger(1f, ref num6);
			}
			float num7 = Sinews.VanillaOverload(player);
			float num8 = ((num7 > 0f) ? (player.m_encumberedStaminaDrain / num7) : 1f);
			return "running x" + num.ToString("0.00", CultureInfo.InvariantCulture) + "  delay " + num5.ToString("0.00", CultureInfo.InvariantCulture) + "s off (" + player.m_staminaRegenDelay.ToString("0.00", CultureInfo.InvariantCulture) + "s of " + Sinews.VanillaDelay(player).ToString("0.00", CultureInfo.InvariantCulture) + "s)  jump height x" + num4.ToString("0.00", CultureInfo.InvariantCulture) + " (push " + ((Character)player).m_jumpForce.ToString("0.00", CultureInfo.InvariantCulture) + " of " + num2.ToString("0.00", CultureInfo.InvariantCulture) + ")  stagger x" + num6.ToString("0.00", CultureInfo.InvariantCulture) + "  overloaded x" + num8.ToString("0.00", CultureInfo.InvariantCulture);
		}

		private static string Armour(Player player)
		{
			float num = 0f;
			Inventory inventory = ((Humanoid)player).GetInventory();
			if (inventory != null)
			{
				foreach (ItemData equippedItem in inventory.GetEquippedItems())
				{
					if (equippedItem != null && equippedItem.m_shared != null && !(equippedItem.m_shared.m_armor <= 0f))
					{
						num += equippedItem.GetArmor();
					}
				}
			}
			float bodyArmor = ((Character)player).GetBodyArmor();
			float num2 = ((num > 0f) ? (bodyArmor / num) : 1f);
			float num3 = Effects.TotalFor("m_armorMultiplier");
			float value = Effects.TotalFor("m_addArmor");
			string text = "armour " + bodyArmor.ToString("0.0", CultureInfo.InvariantCulture) + "  bare " + num.ToString("0.0", CultureInfo.InvariantCulture) + "  game x" + num2.ToString("0.00", CultureInfo.InvariantCulture) + "  rist x" + (1f + num3).ToString("0.00", CultureInfo.InvariantCulture);
			if (Math.Abs(value) > 0.0001f)
			{
				text = text + " and +" + value.ToString("0.#", CultureInfo.InvariantCulture) + " flat";
			}
			return text;
		}

		private static void Rank(Terminal term, ConsoleEventArgs args)
		{
			if (args.Length < 4)
			{
				term.AddString("rist rank <card> <n>");
				return;
			}
			if (!Net.IsServer)
			{
				term.AddString("rist: ranks live on the server, so this only works in singleplayer or when you host.");
				return;
			}
			string text = args[2];
			Card card = Cards.Get(text);
			if (card == null)
			{
				term.AddString("rist: no card called '" + text + "'.");
				return;
			}
			if (!int.TryParse(args[3], NumberStyles.Integer, CultureInfo.InvariantCulture, out var result) || result < 0)
			{
				term.AddString("rist: '" + args[3] + "' is not a rank.");
				return;
			}
			int value = RistConfig.MaxRank.Value;
			if (result > value)
			{
				term.AddString("rist: MaxRank is " + value + ".");
				return;
			}
			if (!Net.LocalOwner(out var peer, out var owner))
			{
				term.AddString("rist: this character has no record yet - wait a moment after spawning.");
				return;
			}
			RistRecord ristRecord = Ledger.For(owner);
			if (ristRecord == null)
			{
				term.AddString("rist: no ledger record for " + owner + ".");
				return;
			}
			int num = ristRecord.RankOf(text);
			ristRecord.SetRank(text, result);
			Ledger.Touch();
			Net.PushState(peer, ristRecord);
			term.AddString(card.Name + ": rank " + num + " -> " + ristRecord.RankOf(text) + ". " + ristRecord.DraftsTaken + " picks spent, " + ristRecord.Owed + " owed.");
		}
	}
	internal static class Effects
	{
		private const string EffectName = "Rist";

		private static readonly int EffectHash = StringExtensionMethods.GetStableHashCode("Rist");

		private static SE_Stats _applied;

		private static string _appliedSignature;

		private static Player _appliedTo;

		private static readonly Dictionary<string, string[]> Spread = new Dictionary<string, string[]>
		{
			{
				"*stamina:move",
				new string[5] { "m_runStaminaDrainModifier", "m_jumpStaminaUseModifier", "m_dodgeStaminaUseModifier", "m_swimStaminaUseModifier", "m_sneakStaminaUseModifier" }
			},
			{
				"*stamina:fight",
				new string[2] { "m_attackStaminaUseModifier", "m_blockStaminaUseModifier" }
			}
		};

		internal static int ExtraRows { get; private set; }

		internal static void Reset()
		{
			if ((Object)(object)_applied != (Object)null)
			{
				Object.Destroy((Object)(object)_applied);
			}
			_applied = null;
			_appliedSignature = null;
			_appliedTo = null;
			Sinews.Reset();
		}

		internal static void Apply(Player player, Dictionary<string, int> ranks)
		{
			if ((Object)(object)player == (Object)null)
			{
				return;
			}
			string text = Signature(ranks);
			if (player != _appliedTo || !(text == _appliedSignature))
			{
				_appliedTo = player;
				_appliedSignature = text;
				ApplyStats(player, ranks);
				ApplyInventoryRows(player, ranks);
				ApplyHorizon(ranks);
				ApplySinews(player, ranks);
				if (RistConfig.Verbose.Value)
				{
					RistPlugin.Log.LogInfo((object)("Applied cards: " + ((text.Length == 0) ? "(none)" : text)));
				}
			}
		}

		private static Dictionary<string, float> Totals(Dictionary<string, int> ranks)
		{
			Dictionary<string, float> dictionary = new Dictionary<string, float>();
			if (ranks == null)
			{
				return dictionary;
			}
			foreach (KeyValuePair<string, int> rank in ranks)
			{
				if (rank.Value <= 0)
				{
					continue;
				}
				Card card = Cards.Get(rank.Key);
				if (card == null)
				{
					continue;
				}
				Add(dictionary, card.Effect, card.PerRank * (float)rank.Value);
				if (card.HasBonus)
				{
					int num = Card.BonusTimes(rank.Value);
					if (num > 0)
					{
						Add(dictionary, card.BonusEffect, card.BonusPerRank * (float)num);
					}
				}
			}
			return dictionary;
		}

		private static void Add(Dictionary<string, float> totals, string effect, float amount)
		{
			if (string.IsNullOrEmpty(effect))
			{
				return;
			}
			if (Spread.TryGetValue(effect, out var value))
			{
				string[] array = value;
				foreach (string effect2 in array)
				{
					Add(totals, effect2, amount);
				}
			}
			else
			{
				totals.TryGetValue(effect, out var value2);
				totals[effect] = value2 + amount;
			}
		}

		internal static Dictionary<string, float> TotalsFor(Dictionary<string, int> ranks)
		{
			return Totals(ranks);
		}

		internal static float TotalFor(string effect)
		{
			if (!ClientState.Known || string.IsNullOrEmpty(effect))
			{
				return 0f;
			}
			if (!Totals(ClientState.Ranks).TryGetValue(effect, out var value))
			{
				return 0f;
			}
			return value;
		}

		private static void ApplyStats(Player player, Dictionary<string, int> ranks)
		{
			//IL_00fa: Unknown result type (might be due to invalid IL or missing references)
			//IL_0105: Unknown result type (might be due to invalid IL or missing references)
			//IL_0110: Unknown result type (might be due to invalid IL or missing references)
			SEMan sEMan = ((Character)player).GetSEMan();
			if (sEMan == null)
			{
				return;
			}
			Dictionary<FieldInfo, float> dictionary = new Dictionary<FieldInfo, float>();
			foreach (KeyValuePair<string, float> item in Totals(ranks))
			{
				if (!Card.IsSpecialEffect(item.Key))
				{
					FieldInfo field = typeof(SE_Stats).GetField(item.Key, BindingFlags.Instance | BindingFlags.Public);
					if (!(field == null) && !(field.FieldType != typeof(float)))
					{
						dictionary[field] = item.Value;
					}
				}
			}
			sEMan.RemoveStatusEffect(EffectHash, true);
			if ((Object)(object)_applied != (Object)null)
			{
				Object.Destroy((Object)(object)_applied);
				_applied = null;
			}
			if (dictionary.Count == 0)
			{
				return;
			}
			SE_Stats val = ScriptableObject.CreateInstance<SE_Stats>();
			((Object)val).name = "Rist";
			((StatusEffect)val).m_name = "Rist";
			((StatusEffect)val).m_ttl = 0f;
			val.m_raiseSkill = (SkillType)999;
			val.m_modifyAttackSkill = (SkillType)999;
			val.m_skillLevel = (SkillType)999;
			foreach (KeyValuePair<FieldInfo, float> item2 in dictionary)
			{
				float num = (Card.IsOneBased(item2.Key.Name) ? (1f + item2.Value) : item2.Value);
				item2.Key.SetValue(val, num);
			}
			sEMan.AddStatusEffect((StatusEffect)(object)val, false, 0, 0f, (short)(-1));
			_applied = val;
		}

		private static void ApplyInventoryRows(Player player, Dictionary<string, int> ranks)
		{
			Totals(ranks).TryGetValue("*inventoryrow", out var value);
			ExtraRows = Mathf.Max(0, Mathf.RoundToInt(value));
			if (RistPlugin.CorePresent)
			{
				ClaimThroughCore(ExtraRows);
			}
			else
			{
				OwnInventoryRows.Claimed = ExtraRows;
			}
		}

		private static void ApplyHorizon(Dictionary<string, int> ranks)
		{
			Dictionary<string, float> dictionary = Totals(ranks);
			dictionary.TryGetValue("*exploreradius", out var value);
			dictionary.TryGetValue("*windcone", out var value2);
			dictionary.TryGetValue("*rowspeed", out var value3);
			Horizon.ExtraExplore = Mathf.Max(0f, value);
			Horizon.ConeNarrowing = Mathf.Clamp01(value2);
			Horizon.RowBonus = Mathf.Max(0f, value3);
		}

		private static void ApplySinews(Player player, Dictionary<string, int> ranks)
		{
			Dictionary<string, float> dictionary = Totals(ranks);
			dictionary.TryGetValue("*staminadelay", out var value);
			dictionary.TryGetValue("*jumpheight", out var value2);
			dictionary.TryGetValue("*overloaded", out var value3);
			Sinews.DelayCut = Mathf.Max(0f, value);
			Sinews.JumpBonus = Mathf.Max(0f, value2);
			Sinews.OverloadMod = Mathf.Min(0f, value3);
			Sinews.Apply(player);
		}

		[MethodImpl(MethodImplOptions.NoInlining)]
		private static void ClaimThroughCore(int rows)
		{
			InventoryRows.Claim("ezomic.valheim.rist", rows);
		}

		private static string Signature(Dictionary<string, int> ranks)
		{
			if (ranks == null || ranks.Count == 0)
			{
				return "";
			}
			List<string> list = new List<string>(ranks.Keys);
			list.Sort();
			StringBuilder stringBuilder = new StringBuilder();
			foreach (string item in list)
			{
				if (stringBuilder.Length > 0)
				{
					stringBuilder.Append(',');
				}
				stringBuilder.Append(item).Append(':').Append(ranks[item]);
			}
			return stringBuilder.ToString();
		}
	}
	internal static class Gate
	{
		private static readonly HashSet<string> Baselined = new HashSet<string>(StringComparer.Ordinal);

		internal static bool HasBaseline(string owner)
		{
			if (owner != null)
			{
				return Baselined.Contains(owner);
			}
			return false;
		}

		internal static void Forget()
		{
			Baselined.Clear();
		}

		internal static string LocalSkills()
		{
			//IL_006f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0079: Expected I4, but got Unknown
			Player localPlayer = Player.m_localPlayer;
			if ((Object)(object)localPlayer == (Object)null)
			{
				return "";
			}
			Skills skills = ((Character)localPlayer).GetSkills();
			if ((Object)(object)skills == (Object)null)
			{
				return "";
			}
			StringBuilder stringBuilder = new StringBuilder();
			foreach (Skill skill in skills.GetSkillList())
			{
				if (skill != null && skill.m_info != null)
				{
					if (stringBuilder.Length > 0)
					{
						stringBuilder.Append(',');
					}
					stringBuilder.Append((int)skill.m_info.m_skill).Append(':').Append(skill.m_level.ToString("R", CultureInfo.InvariantCulture));
				}
			}
			return stringBuilder.ToString();
		}

		internal static void Judge(long sender, string owner, string facts, string skills)
		{
			if (!RistConfig.CheckSkillBaseline.Value || string.IsNullOrEmpty(owner))
			{
				return;
			}
			Dictionary<int, float> dictionary = ParseSkills(skills);
			if (dictionary == null)
			{
				return;
			}
			RistRecord ristRecord = Ledger.For(owner);
			if (ristRecord == null)
			{
				return;
			}
			bool flag = !ristRecord.HasSnapshot;
			Baselined.Add(owner);
			int num = 0;
			foreach (KeyValuePair<int, float> item in dictionary)
			{
				if (!ristRecord.Snapshot.TryGetValue(item.Key, out var value) || !(item.Value <= value))
				{
					ristRecord.Snapshot[item.Key] = item.Value;
					num++;
				}
			}
			if (num > 0)
			{
				Ledger.Touch();
			}
			if (flag)
			{
				RistPlugin.Log.LogInfo((object)("Baseline adopted for " + owner + " (" + dictionary.Count + " skills)."));
			}
			else if (num > 0 && RistConfig.Verbose.Value)
			{
				RistPlugin.Log.LogInfo((object)("Baseline for " + owner + " raised on " + num + " skill(s)."));
			}
			bool flag2 = Recompute(owner, ristRecord);
			if (Credit(sender, owner, ristRecord, dictionary) || flag2)
			{
				Net.PushState(sender, ristRecord);
			}
		}

		internal static bool Recompute(string owner, RistRecord rec)
		{
			if (rec == null)
			{
				return false;
			}
			string text = (RistConfig.WeightGeneration.Value ?? "").Replace("|", "/").Trim();
			if ((rec.WeightGen ?? "") == text)
			{
				return false;
			}
			if (!rec.HasSnapshot)
			{
				return false;
			}
			float xp = rec.Xp;
			int level = rec.Level;
			rec.Xp = Weights.WorthOf(rec.Snapshot);
			rec.WeightGen = text;
			Ledger.Touch();
			RistPlugin.Log.LogInfo((object)("Re-priced " + owner + " at weight generation '" + text + "': " + xp.ToString("0") + " -> " + rec.Xp.ToString("0") + " xp, Rist level " + level + " -> " + rec.Level + " (" + rec.DraftsTaken + " cards kept, " + rec.Owed + " to spend)."));
			return true;
		}

		private static bool Credit(long sender, string owner, RistRecord rec, Dictionary<int, float> reported)
		{
			if (!RistConfig.CreditExistingSkills.Value)
			{
				return false;
			}
			float num = Weights.WorthOf(reported);
			if (num <= rec.Xp + 0.001f)
			{
				return false;
			}
			int level = rec.Level;
			rec.Xp = num;
			Ledger.Touch();
			RistPlugin.Log.LogInfo((object)("Credited " + owner + " for skills already held: " + num.ToString("0") + " xp, Rist level " + level + " -> " + rec.Level + "."));
			return true;
		}

		private static Dictionary<int, float> ParseSkills(string wire)
		{
			if (wire == null)
			{
				return null;
			}
			Dictionary<int, float> dictionary = new Dictionary<int, float>();
			string[] array = wire.Split(new char[1] { ',' });
			foreach (string text in array)
			{
				if (text.Length != 0)
				{
					string[] array2 = text.Split(new char[1] { ':' });
					if (array2.Length == 2 && int.TryParse(array2[0], out var result) && float.TryParse(array2[1], NumberStyles.Float, CultureInfo.InvariantCulture, out var result2))
					{
						dictionary[result] = result2;
					}
				}
			}
			return dictionary;
		}
	}
	internal static class Horizon
	{
		[HarmonyPatch(typeof(Minimap), "Explore", new Type[]
		{
			typeof(Vector3),
			typeof(float)
		})]
		internal static class Radius
		{
			[HarmonyPrefix]
			private static void Widen(ref float radius)
			{
				if (ExtraExplore > 0f)
				{
					radius *= 1f + ExtraExplore;
				}
			}
		}

		[HarmonyPatch(typeof(Ship), "GetWindAngleFactor")]
		internal static class WindAngle
		{
			[HarmonyPostfix]
			private static void Narrow(Ship __instance, ref float __result)
			{
				//IL_0031: Unknown result type (might be due to invalid IL or missing references)
				//IL_003c: Unknown result type (might be due to invalid IL or missing references)
				//IL_0041: Unknown result type (might be due to invalid IL or missing references)
				if (!((Object)(object)__instance == (Object)null) && !((Object)(object)EnvMan.instance == (Object)null) && !(ConeNarrowing <= 0f) && LocalIsHelmsman(__instance))
				{
					float num = Vector3.Dot(EnvMan.instance.GetWindDir(), -((Component)__instance).transform.forward);
					if (!(num <= 0f))
					{
						Edges(out var lo, out var hi);
						float num2 = Mathf.Lerp(0.7f, 1f, 1f - Mathf.Abs(num));
						float num3 = 1f - Mathf.Clamp01((num - lo) / (hi - lo));
						__result = Mathf.Clamp(num2 * num3, __result, num2);
					}
				}
			}
		}

		[HarmonyPatch(typeof(Ship), "GetSailForce")]
		internal static class Sail
		{
			private static FieldRef<Ship, Vector3> _force;

			private static FieldRef<Ship, Vector3> _velocity;

			private static bool _bound;

			private static bool _broken;

			private static bool Bound()
			{
				if (_bound)
				{
					return !_broken;
				}
				_bound = true;
				try
				{
					_force = AccessTools.FieldRefAccess<Ship, Vector3>("m_sailForce");
					_velocity = AccessTools.FieldRefAccess<Ship, Vector3>("m_windChangeVelocity");
				}
				catch (Exception ex)
				{
					_broken = true;
					RistPlugin.Log.LogWarning((object)("Weatherly cannot bend the sail, the ship's sail fields have moved. The dead zone still narrows. " + ex.Message));
				}
				return !_broken;
			}

			[HarmonyPrefix]
			private static bool Bend(Ship __instance, float sailSize, ref Vector3 __result)
			{
				//IL_0033: Unknown result type (might be due to invalid IL or missing references)
				//IL_0038: Unknown result type (might be due to invalid IL or missing references)
				//IL_003f: Unknown result type (might be due to invalid IL or missing references)
				//IL_0044: Unknown result type (might be due to invalid IL or missing references)
				//IL_0045: Unknown result type (might be due to invalid IL or missing references)
				//IL_0046: Unknown result type (might be due to invalid IL or missing references)
				//IL_0047: 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_009a: Unknown result type (might be due to invalid IL or missing references)
				//IL_009b: Unknown result type (might be due to invalid IL or missing references)
				//IL_00a0: Unknown result type (might be due to invalid IL or missing references)
				//IL_00b9: Unknown result type (might be due to invalid IL or missing references)
				//IL_00b2: 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_00c5: Unknown result type (might be due to invalid IL or missing references)
				//IL_00ca: Unknown result type (might be due to invalid IL or missing references)
				//IL_00ce: Unknown result type (might be due to invalid IL or missing references)
				//IL_00de: Unknown result type (might be due to invalid IL or missing references)
				//IL_00e3: Unknown result type (might be due to invalid IL or missing references)
				//IL_0103: Unknown result type (might be due to invalid IL or missing references)
				//IL_0108: 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_011b: Unknown result type (might be due to invalid IL or missing references)
				//IL_0123: Unknown result type (might be due to invalid IL or missing references)
				//IL_0128: Unknown result type (might be due to invalid IL or missing references)
				if ((Object)(object)__instance == (Object)null || (Object)(object)EnvMan.instance == (Object)null)
				{
					return true;
				}
				if (ConeNarrowing <= 0f || !LocalIsHelmsman(__instance))
				{
					return true;
				}
				Vector3 windDir = EnvMan.instance.GetWindDir();
				Vector3 forward = ((Component)__instance).transform.forward;
				float num = Vector3.Dot(windDir, -forward);
				Edges(out var _, out var hi);
				if (num <= 0.75f || num >= hi)
				{
					return true;
				}
				if (!Bound())
				{
					return true;
				}
				float num2 = Mathf.Lerp(0.25f, 1f, EnvMan.instance.GetWindIntensity());
				float num3 = __instance.GetWindAngleFactor() * num2;
				Vector3 val = windDir + forward;
				Vector3 val2 = Vector3.Lerp((((Vector3)(ref val)).sqrMagnitude < 1E-06f) ? forward : ((Vector3)(ref val)).normalized, forward, Mathf.Clamp01(ConeNarrowing));
				Vector3 val3 = ((Vector3)(ref val2)).normalized * (num3 * __instance.m_sailForceFactor * sailSize);
				ref Vector3 reference = ref _force.Invoke(__instance);
				ref Vector3 reference2 = ref _velocity.Invoke(__instance);
				reference = Vector3.SmoothDamp(reference, val3, ref reference2, 1f, 99f);
				__result = reference;
				return false;
			}
		}

		[HarmonyPatch(typeof(Hud), "UpdateShipHud")]
		internal static class Ring
		{
			private static Transform _root;

			private static readonly List<RingWarp> Warps = new List<RingWarp>();

			private static bool _warned;

			[HarmonyPostfix]
			private static void Track(Hud __instance, Player player)
			{
				if ((Object)(object)__instance == (Object)null)
				{
					return;
				}
				RectTransform shipWindIndicatorRoot = __instance.m_shipWindIndicatorRoot;
				if ((Object)(object)shipWindIndicatorRoot == (Object)null || !((Component)shipWindIndicatorRoot).gameObject.activeInHierarchy)
				{
					return;
				}
				if ((Object)(object)shipWindIndicatorRoot != (Object)(object)_root || Stale())
				{
					Find((Transform)(object)shipWindIndicatorRoot);
				}
				float narrowing = 0f;
				if ((Object)(object)player != (Object)null)
				{
					Ship controlledShip = player.GetControlledShip();
					if ((Object)(object)controlledShip != (Object)null && LocalIsHelmsman(controlledShip))
					{
						narrowing = ConeNarrowing;
					}
				}
				foreach (RingWarp warp in Warps)
				{
					if ((Object)(object)warp != (Object)null)
					{
						warp.SetNarrowing(narrowing);
					}
				}
			}

			private static bool Stale()
			{
				foreach (RingWarp warp in Warps)
				{
					if ((Object)(object)warp == (Object)null)
					{
						return true;
					}
				}
				return false;
			}

			private static void Find(Transform root)
			{
				_root = root;
				Warps.Clear();
				Image[] componentsInChildren = ((Component)root).GetComponentsInChildren<Image>(true);
				RingWarp item = default(RingWarp);
				foreach (Image val in componentsInChildren)
				{
					if ((Object)(object)val == (Object)null || (Object)(object)val.sprite == (Object)null)
					{
						continue;
					}
					string name = ((Object)val.sprite).name;
					if (!(name != "ship_circle") || !(name != "ship_circle_bw"))
					{
						if (!((Component)val).TryGetComponent<RingWarp>(ref item))
						{
							item = ((Component)val).gameObject.AddComponent<RingWarp>();
						}
						Warps.Add(item);
					}
				}
				if (Warps.Count <= 0 && !_warned)
				{
					_warned = true;
					RistPlugin.Log.LogWarning((object)"Weatherly cannot redraw the wind ring: no ship_circle or ship_circle_bw sprite under the ship HUD. The dead zone still narrows; the ring will show vanilla's.");
				}
			}
		}

		[HarmonyPatch(typeof(Ship), "CustomFixedUpdate")]
		internal static class Row
		{
			[HarmonyPrefix]
			private static void Scale(Ship __instance, out float __state)
			{
				__state = (((Object)(object)__instance != (Object)null) ? __instance.m_backwardForce : 0f);
				if (!((Object)(object)__instance == (Object)null) && !(RowBonus <= 0f) && __instance.IsOwner() && LocalIsHelmsman(__instance))
				{
					float num = 1f + RowBonus;
					__instance.m_backwardForce = __state * num * num;
				}
			}

			[HarmonyFinalizer]
			private static Exception Restore(Ship __instance, float __state, Exception __exception)
			{
				if ((Object)(object)__instance != (Object)null)
				{
					__instance.m_backwardForce = __state;
				}
				return __exception;
			}
		}

		internal const string ExploreRadius = "*exploreradius";

		internal const string WindCone = "*windcone";

		internal const string RowSpeed = "*rowspeed";

		internal static float ExtraExplore;

		internal static float ConeNarrowing;

		internal static float RowBonus;

		private static readonly float VanillaLiveAngle = Mathf.Acos(0.75f) * 57.29578f;

		private static readonly float VanillaDeadAngle = Mathf.Acos(0.8f) * 57.29578f;

		private const float MaxNarrowing = 0.8f;

		internal const float MaxNarrowingValue = 0.8f;

		private const float VanillaLiveDot = 0.75f;

		internal static float VanillaLiveAngleDeg => VanillaLiveAngle;

		internal static float VanillaDeadAngleDeg => VanillaDeadAngle;

		private static bool LocalIsHelmsman(Ship ship)
		{
			Player localPlayer = Player.m_localPlayer;
			if ((Object)(object)ship == (Object)null || (Object)(object)localPlayer == (Object)null || (Object)(object)ship.m_shipControlls == (Object)null)
			{
				return false;
			}
			return ship.m_shipControlls.GetUser() == localPlayer.GetPlayerID();
		}

		internal static void EdgeAngles(out float liveAngle, out float deadAngle)
		{
			float num = 1f - Mathf.Clamp(ConeNarrowing, 0f, 0.8f);
			liveAngle = VanillaLiveAngle * num;
			deadAngle = VanillaDeadAngle * num;
		}

		private static void Edges(out float lo, out float hi)
		{
			EdgeAngles(out var liveAngle, out var deadAngle);
			lo = Mathf.Cos(liveAngle * ((float)Math.PI / 180f));
			hi = Mathf.Cos(deadAngle * ((float)Math.PI / 180f));
		}
	}
	internal static class HudBar
	{
		private const float BorderBuffer = 16f;

		private static GameObject _root;

		private static RectTransform _rect;

		private static RectTransform _fastBar;

		private static RectTransform _slowBar;

		private static Image _fastFill;

		private static Image _slowFill;

		private static CanvasGroup _group;

		private static TMP_Text _text;

		private static Animator _animator;

		private static Canvas _canvas;

		private static bool _pulsing;

		private static bool _failed;

		private static int _shownLevel = -1;

		private static int _shownPercent = -1;

		private static bool _shownOwed;

		private static float _sizedAt;

		private static float _trail = -1f;

		private const float TrailSpeed = 0.5f;

		private static bool _uprightAt;

		private static string _tintedAt;

		private static readonly Vector3[] Corners = (Vector3[])(object)new Vector3[4];

		private static float _loggedTop = float.NaN;

		internal static bool Live => (Object)(object)_root != (Object)null;

		internal static bool HasText => (Object)(object)_text != (Object)null;

		internal static Vector2? ScreenCentre
		{
			get
			{
				//IL_0029: Unknown result type (might be due to invalid IL or missing references)
				//IL_0042: Unknown result type (might be due to invalid IL or missing references)
				//IL_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_004d: Unknown result type (might be due to invalid IL or missing references)
				//IL_0059: 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)
				if ((Object)(object)_rect == (Object)null)
				{
					return null;
				}
				Vector2 val = RectTransformUtility.WorldToScreenPoint(((Object)(object)_canvas != (Object)null && (int)_canvas.renderMode != 0) ? _canvas.worldCamera : null, ((Transform)_rect).position);
				return new Vector2(val.x, (float)Screen.height - val.y);
			}
		}

		internal static float? ScreenTop
		{
			get
			{
				//IL_0029: 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_007b: Unknown result type (might be due to invalid IL or missing references)
				//IL_007d: 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_008a: Unknown result type (might be due to invalid IL or missing references)
				if ((Object)(object)_rect == (Object)null)
				{
					return null;
				}
				Camera val = (((Object)(object)_canvas != (Object)null && (int)_canvas.renderMode != 0) ? _canvas.worldCamera : null);
				float num = float.MaxValue;
				RectTransform[] componentsInChildren = ((Component)_rect).GetComponentsInChildren<RectTransform>(true);
				for (int i = 0; i < componentsInChildren.Length; i++)
				{
					componentsInChildren[i].GetWorldCorners(Corners);
					Vector3[] corners = Corners;
					foreach (Vector3 val2 in corners)
					{
						Vector2 val3 = RectTransformUtility.WorldToScreenPoint(val, val2);
						float num2 = (float)Screen.height - val3.y;
						if (num2 < num)
						{
							num = num2;
						}
					}
				}
				if (num == float.MaxValue)
				{
					return null;
				}
				if (RistConfig.Verbose.Value && !Mathf.Approximately(num, _loggedTop))
				{
					_loggedTop = num;
					RistPlugin.Log.LogInfo((object)("Bar top edge measured at y=" + Mathf.RoundToInt(num) + " of " + Screen.height + ", canvas scale " + Scale.ToString("0.00") + "."));
				}
				return num;
			}
		}

		internal static float Scale
		{
			get
			{
				if (!((Object)(object)_canvas != (Object)null))
				{
					return 1f;
				}
				return _canvas.scaleFactor;
			}
		}

		internal static float HalfLength
		{
			get
			{
				float num = (((Object)(object)_canvas != (Object)null) ? _canvas.scaleFactor : 1f);
				return Mathf.Max(16f, RistConfig.BarSize.Value) * 0.5f * num;
			}
		}

		internal static void Update()
		{
			if (!RistConfig.Enabled.Value || !RistConfig.ShowXpBar.Value || !RistConfig.VanillaBar.Value)
			{
				Drop();
			}
			else if ((Object)(object)Hud.instance == (Object)null || (Object)(object)Player.m_localPlayer == (Object)null)
			{
				Drop();
			}
			else
			{
				if ((Object)(object)_root == (Object)null && !Build())
				{
					return;
				}
				bool flag = Visible();
				if ((Object)(object)_group != (Object)null)
				{
					_group.alpha = (flag ? 1f : 0f);
				}
				if (flag)
				{
					Place();
					if (!Mathf.Approximately(_sizedAt, RistConfig.BarSize.Value))
					{
						Size();
					}
					if (_uprightAt != RistConfig.BarUpright.Value)
					{
						Lay();
					}
					if (_tintedAt != RistConfig.BarColour.Value)
					{
						Colour();
					}
					float num = Mathf.Clamp01(Levels.Progress(ClientState.Xp));
					Fill(num);
					int num2 = Mathf.Clamp(Mathf.RoundToInt(num * 100f), 0, 100);
					bool hasPick = ClientState.HasPick;
					if ((Object)(object)_text != (Object)null && (_shownLevel != ClientState.Level || _shownPercent != num2 || _shownOwed != hasPick))
					{
						_shownLevel = ClientState.Level;
						_shownPercent = num2;
						_shownOwed = hasPick;
						_text.text = _shownLevel + " · " + num2 + "%" + (ClientState.HasPick ? " ★" : "");
					}
					Announce();
				}
			}
		}

		private static bool Visible()
		{
			if (!ClientState.Known)
			{
				return false;
			}
			if (((Character)Player.m_localPlayer).IsDead())
			{
				return false;
			}
			if (((Character)Player.m_localPlayer).InCutscene())
			{
				return false;
			}
			return !Hud.instance.m_userHidden;
		}

		private static void Announce()
		{
			//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_0066: 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_0073: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)_fastFill == (Object)null)
			{
				return;
			}
			if (!ClientState.HasPick)
			{
				if (_pulsing)
				{
					_pulsing = false;
					Colour();
				}
			}
			else
			{
				_pulsing = true;
				float num = Mathf.Max(1f, RistConfig.BarFlashSeconds.Value);
				float num2 = Mathf.PingPong(Time.time / num * 2f, 1f);
				Color val = RistConfig.BarTint();
				((Graphic)_fastFill).color = Color.Lerp(val, Color.white, num2 * 0.5f);
			}
		}

		private static bool Build()
		{
			if (_failed)
			{
				return false;
			}
			RectTransform val = (((Object)(object)Hud.instance.m_eitrBarRoot != (Object)null) ? Hud.instance.m_eitrBarRoot : Hud.instance.m_staminaBar2Root);
			if ((Object)(object)val == (Object)null || (Object)(object)((Transform)val).parent == (Object)null)
			{
				Fail("no upright bar to clone from - falling back to the plain bar.");
				return false;
			}
			GameObject val2 = Object.Instantiate<GameObject>(((Component)val).gameObject, ((Transform)val).parent);
			((Object)val2).name = "RistXpBar";
			val2.SetActive(true);
			_rect = val2.GetComponent<RectTransform>();
			_canvas = val2.GetComponentInParent<Canvas>();
			GuiBar val3 = null;
			GuiBar val4 = null;
			GuiBar[] componentsInChildren = val2.GetComponentsInChildren<GuiBar>(true);
			foreach (GuiBar val5 in componentsInChildren)
			{
				if (val5.m_smoothDrain || val5.m_smoothFill)
				{
					if ((Object)(object)val4 == (Object)null)
					{
						val4 = val5;
					}
				}
				else if ((Object)(object)val3 == (Object)null)
				{
					val3 = val5;
				}
			}
			if ((Object)(object)val3 == (Object)null)
			{
				val3 = val4;
			}
			if ((Object)(object)val4 == (Object)null)
			{
				val4 = val3;
			}
			if ((Object)(object)val3 == (Object)null)
			{
				Object.Destroy((Object)(object)val2);
				Fail("the cloned bar has no GuiBar - falling back to the plain bar.");
				return false;
			}
			_fastBar = val3.m_bar;
			_slowBar = val4.m_bar;
			_fastFill = (((Object)(object)_fastBar != (Object)null) ? ((Component)_fastBar).GetComponent<Image>() : null);
			_slowFill = (((Object)(object)_slowBar != (Object)null) ? ((Component)_slowBar).GetComponent<Image>() : null);
			if ((Object)(object)val4 != (Object)(object)val3)
			{
				Object.DestroyImmediate((Object)(object)val4);
			}
			Object.DestroyImmediate((Object)(object)val3);
			_text = val2.GetComponentInChildren<TMP_Text>(true);
			_animator = val2.GetComponent<Animator>();
			if ((Object)(object)_animator == (Object)null)
			{
				_animator = val2.GetComponentInChildren<Animator>(true);
			}
			Size();
			Colour();
			Lay();
			_group = val2.GetComponent<CanvasGroup>();
			if ((Object)(object)_group == (Object)null)
			{
				_group = val2.AddComponent<CanvasGroup>();
			}
			_group.blocksRaycasts = false;
			_group.interactable = false;
			Unfade(val2);
			_root = val2;
			_shownLevel = -1;
			_trail = -1f;
			RistPlugin.Log.LogInfo((object)("Experience bar cloned from " + ((Object)val).name + "."));
			return true;
		}

		private static void Unfade(GameObject go)
		{
			//IL_009a: Unknown result type (might be due to invalid IL or missing references)
			//IL_009f: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a1: 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 ((Object)(object)_animator != (Object)null)
			{
				Object.Destroy((Object)(object)_animator);
			}
			_animator = null;
			Transform[] componentsInChildren = go.GetComponentsInChildren<Transform>(true);
			foreach (Transform val in componentsInChildren)
			{
				if (!((Component)val).gameObject.activeSelf)
				{
					((Component)val).gameObject.SetActive(true);
				}
			}
			CanvasGroup[] componentsInChildren2 = go.GetComponentsInChildren<CanvasGroup>(true);
			foreach (CanvasGroup val2 in componentsInChildren2)
			{
				if (val2.alpha < 0.05f)
				{
					val2.alpha = 1f;
				}
			}
			Graphic[] componentsInChildren3 = go.GetComponentsInChildren<Graphic>(true);
			foreach (Graphic val3 in componentsInChildren3)
			{
				Color color = val3.color;
				if (!(color.a >= 0.05f))
				{
					color.a = 1f;
					val3.color = color;
				}
			}
			if (RistConfig.Verbose.Value)
			{
				RistPlugin.Log.LogInfo((object)"Cloned bar detached from the donor's animator and brought up by hand.");
			}
		}

		private static void Size()
		{
			_sizedAt = RistConfig.BarSize.Value;
			float num = Mathf.Max(16f, _sizedAt);
			_rect.SetSizeWithCurrentAnchors((Axis)0, num + 16f);
			if (RistConfig.Verbose.Value)
			{
				RistPlugin.Log.LogInfo((object)("Bar sized to " + num + "."));
			}
		}

		private static void Fill(float progress)
		{
			float num = Mathf.Max(16f, RistConfig.BarSize.Value);
			if (_trail < 0f || progress > _trail)
			{
				_trail = progress;
			}
			else
			{
				_trail = Mathf.MoveTowards(_trail, progress, Time.deltaTime * 0.5f);
			}
			Draw(_slowBar, num * Mathf.Max(_trail, progress));
			Draw(_fastBar, num * progress);
		}

		private static void Draw(RectTransform bar, float width)
		{
			if (!((Object)(object)bar == (Object)null))
			{
				bar.SetSizeWithCurrentAnchors((Axis)0, Mathf.Max(0f, width));
			}
		}

		private static void Lay()
		{
			//IL_003f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0029: Unknown result type (might be due to invalid IL or missing references)
			_uprightAt = RistConfig.BarUpright.Value;
			if (!((Object)(object)_rect == (Object)null))
			{
				((Transform)_rect).rotation = (_uprightAt ? Quaternion.Euler(0f, 0f, 90f) : Quaternion.identity);
			}
		}

		private static void Colour()
		{
			//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_001a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0025: 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_003d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0049: Unknown result type (might be due to invalid IL or missing references)
			//IL_004f: Unknown result type (might be due to invalid IL or missing references)
			//IL_006b: Unknown result type (might be due to invalid IL or missing references)
			_tintedAt = RistConfig.BarColour.Value;
			Color val = RistConfig.BarTint();
			Tint(_fastFill, val);
			Tint(_slowFill, new Color(val.r * 0.55f, val.g * 0.55f, val.b * 0.55f, val.a));
			if ((Object)(object)_text != (Object)null)
			{
				((Graphic)_text).color = val;
			}
		}

		private unsafe static void Tint(Image image, Color colour)
		{
			//IL_00be: 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_001f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0052: Unknown result type (might be due to invalid IL or missing references)
			//IL_0057: Unknown result type (might be due to invalid IL or missing references)
			//IL_009b: Unknown result type (might be due to invalid IL or missing references)
			//IL_009c: Unknown result type (might be due to invalid IL or missing references)
			if (!((Object)(object)image == (Object)null))
			{
				if (RistConfig.Verbose.Value && ((Graphic)image).color != colour)
				{
					ManualLogSource log = RistPlugin.Log;
					string[] obj = new string[9]
					{
						"Bar fill '",
						((Object)image).name,
						"' was ",
						((object)((Graphic)image).color/*cast due to .constrained prefix*/).ToString(),
						" (sprite ",
						((Object)(object)image.sprite != (Object)null) ? ((Object)image.sprite).name : "none",
						"); set to ",
						null,
						null
					};
					Color val = colour;
					obj[7] = ((object)(*(Color*)(&val))/*cast due to .constrained prefix*/).ToString();
					obj[8] = ".";
					log.LogInfo((object)string.Concat(obj));
				}
				((Graphic)image).color = colour;
			}
		}

		private static void Place()
		{
			//IL_0077: Unknown result type (might be due to invalid IL or missing references)
			//IL_0127: Unknown result type (might be due to invalid IL or missing references)
			//IL_0138: 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)
			//IL_00f0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f9: Unknown result type (might be due to invalid IL or missing references)
			//IL_010d: Unknown result type (might be due to invalid IL or missing references)
			Transform parent = ((Transform)_rect).parent;
			RectTransform val = (RectTransform)(object)((parent is RectTransform) ? parent : null);
			if ((Object)(object)val == (Object)null)
			{
				return;
			}
			bool flag = ((Object)(object)Hud.instance.m_buildHud != (Object)null && Hud.instance.m_buildHud.activeSelf) || ((Object)(object)Hud.instance.m_shipHudRoot != (Object)null && Hud.instance.m_shipHudRoot.activeSelf);
			Camera val2 = (((Object)(object)_canvas != (Object)null && (int)_canvas.renderMode != 0) ? _canvas.worldCamera : null);
			float scale = Scale;
			Vector2 val3 = default(Vector2);
			((Vector2)(ref val3))..ctor(RistConfig.BarPosX.Value * scale, (RistConfig.BarPosY.Value + (flag ? RistConfig.BarBuildRaise.Value : 0f)) * scale);
			if (RistConfig.BarFollowStamina.Value)
			{
				RectTransform staminaBar2Root = Hud.instance.m_staminaBar2Root;
				if ((Object)(object)staminaBar2Root != (Object)null)
				{
					Vector2 val4 = RectTransformUtility.WorldToScreenPoint(val2, ((Transform)staminaBar2Root).position);
					((Vector2)(ref val3))..ctor(val4.x + RistConfig.BarOffsetX.Value * scale, val4.y - RistConfig.BarOffsetY.Value * scale);
				}
			}
			Vector3 position = default(Vector3);
			if (RectTransformUtility.ScreenPointToWorldPointInRectangle(val, val3, val2, ref position))
			{
				((Transform)_rect).position = position;
			}
		}

		private static void Fail(string why)
		{
			_failed = true;
			RistPlugin.Log.LogWarning((object)("Rist: " + why));
		}

		internal static void Drop()
		{
			if ((Object)(object)_root != (Object)null)
			{
				Object.Destroy((Object)(object)_root);
			}
			_root = null;
			_rect = null;
			_fastBar = null;
			_slowBar = null;
			_fastFill = null;
			_slowFill = null;
			_group = null;
			_pulsing = false;
			_text = null;
			_animator = null;
			_canvas = null;
			_shownLevel = -1;
			_trail = -1f;
			_shownPercent = -1;
			_sizedAt = 0f;
			_tintedAt = null;
			_failed = false;
		}
	}
	internal static class InfoTab
	{
		[CompilerGenerated]
		private static class <>O
		{
			public static UnityAction <0>__Open;
		}

		private static GameObject _tab;

		private static InventoryGui _seen;

		private static Sprite _icon;

		private static bool _failed;

		internal static void Update()
		{
			if (!_failed && RistConfig.Enabled.Value && RistConfig.ShowInfoTab.Value)
			{
				InventoryGui instance = InventoryGui.instance;
				if ((Object)(object)instance == (Object)null || (Object)(object)instance.m_infoPanel == (Object)null)
				{
					_seen = null;
				}
				else if (instance != _seen || !((Object)(object)_tab != (Object)null))
				{
					_seen = instance;
					Build(instance);
				}
			}
		}

		private static void Build(InventoryGui gui)
		{
			//IL_0079: Unknown result type (might be due to invalid IL or missing references)
			//IL_007e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0084: Expected O, but got Unknown
			Button val = Donor(gui);
			if ((Object)(object)val == (Object)null)
			{
				_failed = true;
				RistPlugin.Log.LogWarning((object)"No compendium tab to clone, and there is no keybind any more - the rists panel has no way in. That is a bug rather than a setting; please report it.");
				return;
			}
			GameObject val2 = Object.Instantiate<GameObject>(((Component)val).gameObject, ((Component)val).transform.parent);
			((Object)val2).name = "RistTab";
			Button component = val2.GetComponent<Button>();
			if ((Object)(object)component != (Object)null)
			{
				((UnityEventBase)component.onClick).RemoveAllListeners();
				ButtonClickedEvent onClick = component.onClick;
				object obj = <>O.<0>__Open;
				if (obj == null)
				{
					UnityAction val3 = Open;
					<>O.<0>__Open = val3;
					obj = (object)val3;
				}
				((UnityEvent)onClick).AddListener((UnityAction)obj);
			}
			Localize[] componentsInChildren = val2.GetComponentsInChildren<Localize>(true);
			for (int i = 0; i < componentsInChildren.Length; i++)
			{
				Object.Destroy((Object)(object)componentsInChildren[i]);
			}
			Icon(val2, val);
			Place(gui, val2, val);
			_tab = val2;
			RistPlugin.Log.LogInfo((object)"Rists tab added to the compendium bar.");
		}

		private static Button Donor(InventoryGui gui)
		{
			//IL_0050: 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)
			Button result = null;
			float num = float.MinValue;
			Button[] componentsInChildren = ((Component)gui.m_infoPanel).GetComponentsInChildren<Button>(true);
			foreach (Button val in componentsInChildren)
			{
				if (!((Object)(object)val == (Object)null) && !(((Object)val).name == "RistTab"))
				{
					RectTransform component = ((Component)val).GetComponent<RectTransform>();
					if (!((Object)(object)component == (Object)null) && component.anchoredPosition.x > num)
					{
						num = component.anchoredPosition.x;
						result = val;
					}
				}
			}
			return result;
		}

		private static void Place(InventoryGui gui, GameObject tab, Button donor)
		{
			//IL_00bb: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f0: Unknown result type (might be due to invalid IL or missing references)
			RectTransform component = tab.GetComponent<RectTransform>();
			if ((Object)(object)component == (Object)null)
			{
				return;
			}
			List<RectTransform> list = new List<RectTransform>();
			Selectable[] componentsInChildren = ((Component)gui.m_infoPanel).GetComponentsInChildren<Selectable>(true);
			foreach (Selectable val in componentsInChildren)
			{
				if (!((Object)(object)val == (Object)null))
				{
					RectTransform component2 = ((Component)val).GetComponent<RectTransform>();
					if (!((Object)(object)component2 == (Object)null) && !((Object)(object)component2 == (Object)(object)component) && !list.Contains(component2))
					{
						list.Add(component2);
					}
				}
			}
			if (list.Count != 0)
			{
				list.Sort((RectTransform a, RectTransform b) => a.anchoredPosition.x.CompareTo(b.anchoredPosition.x));
				list.Add(component);
				float num = Pitch(list);
				float y = list[0].anchoredPosition.y;
				float num2 = (0f - num) * (float)(list.Count - 1) * 0.5f;
				for (int num3 = 0; num3 < list.Count; num3++)
				{
					list[num3].anchoredPosition = new Vector2(num2 + (float)num3 * num, y);
				}
				RistPlugin.Log.LogInfo((object)("Compendium bar re-spaced: " + list.Count + " icons at " + (int)num + "px."));
			}
		}

		private static float Pitch(List<RectTransform> icons)
		{
			//IL_000b: Unknown result type (might be due to invalid IL or missing references)
			//IL_001e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0048: 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)
			for (int i = 1; i < icons.Count; i++)
			{
				float num = icons[i].anchoredPosition.x - icons[i - 1].anchoredPosition.x;
				if (num > 1f)
				{
					return num;
				}
			}
			Rect rect = icons[0].rect;
			return ((Rect)(ref rect)).width * 1.15f;
		}

		private static void Icon(GameObject tab, Button donor)
		{
			//IL_002c: Unknown result type (might be due to invalid IL or missing references)
			//IL_003b: Unknown result type (might be due to invalid IL or missing references)
			//IL_015b: Unknown result type (might be due to invalid IL or missing references)
			//IL_02a7: Unknown result type (might be due to invalid IL or missing references)
			//IL_02ac: Unknown result type (might be due to invalid IL or missing references)
			//IL_02b0: Unknown result type (might be due to invalid IL or missing references)
			//IL_02b5: Unknown result type (might be due to invalid IL or missing references)
			//IL_02d0: Unknown result type (might be due to invalid IL or missing references)
			//IL_02d5: Unknown result type (might be due to invalid IL or missing references)
			//IL_02d9: Unknown result type (might be due to invalid IL or missing references)
			//IL_02de: Unknown result type (might be due to invalid IL or missing references)
			//IL_034f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0354: Unknown result type (might be due to invalid IL or missing references)
			//IL_0358: Unknown result type (might be due to invalid IL or missing references)
			//IL_035d: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ec: Unknown result type (might be due to invalid IL or missing references)
			//IL_01f9: Unknown result type (might be due to invalid IL or missing references)
			//IL_01fe: Unknown result type (might be due to invalid IL or missing references)
			//IL_0202: Unknown result type (might be due to invalid IL or missing references)
			//IL_0207: Unknown result type (might be due to invalid IL or missing references)
			//IL_0223: Unknown result type (might be due to invalid IL or missing references)
			//IL_0228: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)_icon == (Object)null)
			{
				Texture2D val = Algiz();
				_icon = Sprite.Create(val, new Rect(0f, 0f, (float)((Texture)val).width, (float)((Texture)val).height), new Vector2(0.5f, 0.5f));
				((Object)_icon).name = "RistAlgiz";
			}
			Button component = tab.GetComponent<Button>();
			Image val2 = (Image)(((Object)(object)component != (Object)null) ? /*isinst with value type is only supported in some contexts*/: null);
			Rect rect;
			Image[] componentsInChildren;
			if ((Object)(object)val2 == (Object)null)
			{
				componentsInChildren = tab.GetComponentsInChildren<Image>(true);
				foreach (Image val3 in componentsInChildren)
				{
					if ((Object)(object)val3 == (Object)null || (Object)(object)((Component)val3).gameObject == (Object)(object)tab || ((Object)val3).name == "Background")
					{
						continue;
					}
					if (!((Object)(object)val2 == (Object)null))
					{
						rect = ((Graphic)val3).rectTransform.rect;
						float width = ((Rect)(ref rect)).width;
						rect = ((Graphic)val2).rectTransform.rect;
						if (!(width < ((Rect)(ref rect)).width))
						{
							continue;
						}
					}
					val2 = val3;
				}
				RistPlugin.Log.LogWarning((object)("The tab's target graphic was not an Image; fell back to '" + (((Object)(object)val2 != (Object)null) ? ((Object)val2).name : "nothing") + "'."));
			}
			if ((Object)(object)val2 == (Object)null)
			{
				RistPlugin.Log.LogWarning((object)"No glyph layer on the cloned tab - it will still open the rists, wearing the donor's picture.");
				return;
			}
			val2.sprite = _icon;
			((Graphic)val2).color = Color.white;
			val2.preserveAspect = true;
			((Behaviour)val2).enabled = true;
			((Graphic)val2).raycastTarget = true;
			if (!RistConfig.Verbose.Value)
			{
				return;
			}
			componentsInChildren = ((Component)donor).GetComponentsInChildren<Image>(true);
			foreach (Image val4 in componentsInChildren)
			{
				if (!((Object)(object)val4 == (Object)null))
				{
					ManualLogSource log = RistPlugin.Log;
					string[] obj = new string[10]
					{
						"  donor layer '",
						((Object)val4).name,
						"' ",
						((Behaviour)val4).enabled ? "on" : "off",
						" ",
						null,
						null,
						null,
						null,
						null
					};
					rect = ((Graphic)val4).rectTransform.rect;
					obj[5] = ((object)((Rect)(ref rect)).size/*cast due to .constrained prefix*/).ToString();
					obj[6] = " colour ";
					obj[7] = ((object)((Graphic)val4).color/*cast due to .constrained prefix*/).ToString();
					obj[8] = " sprite ";
					obj[9] = (((Object)(object)val4.sprite != (Object)null) ? ((Object)val4.sprite).name : "none");
					log.LogInfo((object)string.Concat(obj));
				}
			}
			ManualLogSource log2 = RistPlugin.Log;
			string[] obj2 = new string[8]
			{
				"  donor '",
				((Object)donor).name,
				"' normal ",
				null,
				null,
				null,
				null,
				null
			};
			ColorBlock colors = ((Selectable)donor).colors;
			obj2[3] = ((object)((ColorBlock)(ref colors)).normalColor/*cast due to .constrained prefix*/).ToString();
			obj2[4] = ", highlighted ";
			colors = ((Selectable)donor).colors;
			obj2[5] = ((object)((ColorBlock)(ref colors)).highlightedColor/*cast due to .constrained prefix*/).ToString();
			obj2[6] = ", target graphic ";
			obj2[7] = (((Object)(object)((Selectable)donor).targetGraphic != (Object)null) ? ((Object)((Selectable)donor).targetGraphic).name : "none");
			log2.LogInfo((object)string.Concat(obj2));
			ManualLogSource log3 = RistPlugin.Log;
			string[] obj3 = new string[5]
			{
				"Rists tab: rune on '",
				((Object)val2).name,
				"' at ",
				null,
				null
			};
			rect = ((Graphic)val2).rectTransform.rect;
			obj3[3] = ((object)((Rect)(ref rect)).size/*cast due to .constrained prefix*/).ToString();
			obj3[4] = ", gold from the button.";
			log3.LogInfo((object)string.Concat(obj3));
		}

		private static Texture2D Algiz()
		{
			//IL_0055: Unknown result type (might be due to invalid IL or missing references)
			//IL_005a: Unknown result type (might be due to invalid IL or missing references)
			//IL_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_0071: Expected O, but got Unknown
			//IL_00b1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e0: Unknown result type (might be due to invalid IL or missing references)
			//IL_0126: Unknown result type (might be due to invalid IL or missing references)
			float[][] array = new float[3][]
			{
				new float[4] { 0.5f, 0.94f, 0.5f, 0.08f },
				new float[4] { 0.5f, 0.52f, 0.1f, 0.1f },
				new float[4] { 0.5f, 0.52f, 0.9f, 0.1f }
			};
			float num = 5.76f;
			Texture2D val = new Texture2D(128, 128, (TextureFormat)4, false)
			{
				filterMode = (FilterMode)1,
				wrapMode = (TextureWrapMode)1,
				hideFlags = (HideFlags)61
			};
			Vector2 p = default(Vector2);
			for (int i = 0; i < 128; i++)
			{
				for (int j = 0; j < 128; j++)
				{
					((Vector2)(ref p))..ctor((float)j + 0.5f, (float)(127 - i) + 0.5f);
					float num2 = float.MaxValue;
					float[][] array2 = array;
					foreach (float[] array3 in array2)
					{
						float num3 = Distance(p, new Vector2(array3[0] * 128f, array3[1] * 128f), new Vector2(array3[2] * 128f, array3[3] * 128f));
						if (num3 < num2)
						{
							num2 = num3;
						}
					}
					val.SetPixel(j, i, new Color(1f, 1f, 1f, Mathf.Clamp01(num - num2 + 0.5f)));
				}
			}
			val.Apply();
			return val;
		}

		private static float Distance(Vector2 p, Vector2 a, Vector2 b)
		{
			//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_0002: Unknown result type (might be due to invalid IL or missing references)
			//IL_0007: Unknown result type (might be due to invalid IL or missing references)
			//IL_0008: Unknown result type (might be due to invalid IL or missing references)
			//IL_0009: Unknown result type (might be due to invalid IL or missing references)
			//IL_000a: Unknown result type (might be due to invalid IL or missing references)
			//IL_000f: Unknown result type (might be due to invalid IL or missing references)
			//IL_002d: Unknown result type (might be due to invalid IL or missing references)
			//IL_002e: Unknown result type (might be due to invalid IL or missing references)
			//IL_002f: 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_0036: Unknown result type (might be due to invalid IL or missing references)
			Vector2 val = b - a;
			float num = Mathf.Clamp01(Vector2.Dot(p - a, val) / Mathf.Max(0.0001f, ((Vector2)(ref val)).sqrMagnitude));
			return Vector2.Distance(p, a + val * num);
		}

		private static void Open()
		{
			if ((Object)(object)InventoryGui.instance != (Object)null)
			{
				InventoryGui.instance.Hide();
			}
			RistPanel.Open();
		}
	}
	internal static class Ledger
	{
		private static readonly Dictionary<string, RistRecord> _records = new Dictionary<string, RistRecord>();

		private static bool _loaded;

		private static bool _dirty;

		private static float _nextFlush;

		private static bool _readFailed;

		private static bool _saidRefusingToWrite;

		private static bool _reconcileBlocked;

		private static string _reconcileWhy = "";

		private static string LedgerPath => Path.Combine(Paths.ConfigPath, "rist-ledger.txt");

		internal static RistRecord For(string owner)
		{
			Load();
			if (string.IsNullOrEmpty(owner))
			{
				return null;
			}
			if (!_records.TryGetValue(owner, out var value))
			{
				value = new RistRecord
				{
					Owner = owner
				};
				_records[owner] = value;
				_dirty = true;
			}
			return value;
		}

		internal static void Touch()
		{
			_dirty = true;
		}

		private static void Load()
		{
			if (_loaded)
			{
				return;
			}
			_loaded = true;
			if (File.Exists(LedgerPath))
			{
				try
				{
					int num = 0;
					string[] array = File.ReadAllLines(LedgerPath);
					foreach (string text in array)
					{
						if (text.Trim().Length != 0)
						{
							RistRe