Decompiled source of NorthernGemcraft v0.1.2

plugins/NorthernGemcraft.Core.dll

Decompiled 3 hours ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using Microsoft.CodeAnalysis;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETStandard,Version=v2.0", FrameworkDisplayName = ".NET Standard 2.0")]
[assembly: AssemblyCompany("NorthernGemcraft.Core")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0+fb61e20309b17b5c1cca7e2cd23a964eea5e02c8")]
[assembly: AssemblyProduct("NorthernGemcraft.Core")]
[assembly: AssemblyTitle("NorthernGemcraft.Core")]
[assembly: AssemblyVersion("1.0.0.0")]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class IsReadOnlyAttribute : Attribute
	{
	}
}
namespace NorthernGemcraft.Core
{
	public enum Effect
	{
		MaxHealth,
		HealthRegen,
		StaminaRegen,
		EitrRegen,
		PhysicalDamageTaken,
		MoveSpeed,
		CarryWeight,
		RunStamina,
		Durability,
		AttackStamina,
		EitrCost,
		LifeSteal,
		Damage
	}
	public static class Effects
	{
		public static readonly Effect[] All = (Effect[])Enum.GetValues(typeof(Effect));

		public static bool LowerIsBetter(Effect e)
		{
			if (e != Effect.PhysicalDamageTaken && e != Effect.AttackStamina && e != Effect.EitrCost)
			{
				return e == Effect.RunStamina;
			}
			return true;
		}

		public static bool OnItem(Effect e)
		{
			if (e != Effect.Durability)
			{
				return e == Effect.Damage;
			}
			return true;
		}

		public static float Goodness(Effect e, float value)
		{
			if (!LowerIsBetter(e))
			{
				return value;
			}
			return 0f - value;
		}

		public static string Label(Effect e)
		{
			return e switch
			{
				Effect.MaxHealth => "max health", 
				Effect.HealthRegen => "health regen", 
				Effect.StaminaRegen => "stamina regen", 
				Effect.EitrRegen => "eitr regen", 
				Effect.PhysicalDamageTaken => "physical damage taken", 
				Effect.MoveSpeed => "movement speed", 
				Effect.CarryWeight => "carry weight", 
				Effect.RunStamina => "stamina while running", 
				Effect.Durability => "durability", 
				Effect.AttackStamina => "stamina per attack", 
				Effect.EitrCost => "eitr per cast", 
				Effect.LifeSteal => "life steal", 
				Effect.Damage => "damage", 
				_ => e.ToString(), 
			};
		}

		public static string Format(Effect e, float value)
		{
			string text = value.ToString("+0.##;-0.##;0", CultureInfo.InvariantCulture);
			if (e == Effect.LifeSteal)
			{
				text = value.ToString("0.##", CultureInfo.InvariantCulture);
			}
			if (e != Effect.CarryWeight)
			{
				return text + "% " + Label(e);
			}
			return text + " " + Label(e);
		}
	}
	public sealed class EffectList
	{
		public static readonly EffectList Empty = new EffectList(new List<(Effect, float)>());

		public IReadOnlyList<(Effect Effect, float PerGrade)> Entries { get; }

		public bool IsEmpty => Entries.Count == 0;

		public EffectList(IReadOnlyList<(Effect Effect, float PerGrade)> entries)
		{
			Entries = entries;
		}

		public static EffectList Parse(string text, List<string> errors = null)
		{
			List<(Effect, float)> list = new List<(Effect, float)>();
			string[] array = (text ?? "").Split(new char[1] { ',' });
			for (int i = 0; i < array.Length; i++)
			{
				string text2 = array[i].Trim();
				if (text2.Length != 0)
				{
					int num = text2.IndexOf(':');
					if (num <= 0 || !Enum.TryParse<Effect>(text2.Substring(0, num).Trim(), ignoreCase: true, out var result) || !float.TryParse(text2.Substring(num + 1).Trim(), NumberStyles.Float, CultureInfo.InvariantCulture, out var result2) || float.IsNaN(result2) || float.IsInfinity(result2))
					{
						errors?.Add(text2);
					}
					else
					{
						list.Add((result, Math.Max(-100f, Math.Min(100f, result2))));
					}
				}
			}
			return new EffectList(list);
		}

		public override string ToString()
		{
			return string.Join(", ", Entries.Select(((Effect Effect, float PerGrade) e) => string.Format("{0}:{1}", e.Effect, e.PerGrade.ToString("R", CultureInfo.InvariantCulture))));
		}
	}
	public readonly struct Enhancement : IEquatable<Enhancement>
	{
		public const string GemKey = "northerngemcraft.gem";

		public const string GradeKey = "northerngemcraft.grade";

		public static readonly Enhancement Empty = new Enhancement(Gem.None, 0);

		public Gem Gem { get; }

		public int Grade { get; }

		public bool IsEmpty => Gem == Gem.None;

		public Enhancement(Gem gem, int grade)
		{
			Gem = gem;
			Grade = ((gem != Gem.None) ? Math.Max(1, grade) : 0);
		}

		public static Enhancement Read(IReadOnlyDictionary<string, string> data)
		{
			if (data == null || !data.TryGetValue("northerngemcraft.gem", out var value))
			{
				return Empty;
			}
			Gem gem = Gems.Parse(value);
			if (gem == Gem.None)
			{
				return Empty;
			}
			string value2;
			int result;
			int grade = ((!data.TryGetValue("northerngemcraft.grade", out value2) || !int.TryParse(value2, NumberStyles.Integer, CultureInfo.InvariantCulture, out result)) ? 1 : result);
			return new Enhancement(gem, grade);
		}

		public void Write(IDictionary<string, string> data)
		{
			if (IsEmpty)
			{
				data.Remove("northerngemcraft.gem");
				data.Remove("northerngemcraft.grade");
			}
			else
			{
				data["northerngemcraft.gem"] = Gem.ToString();
				data["northerngemcraft.grade"] = Grade.ToString(CultureInfo.InvariantCulture);
			}
		}

		public static string Roman(int grade)
		{
			return grade switch
			{
				1 => "I", 
				2 => "II", 
				3 => "III", 
				4 => "IV", 
				5 => "V", 
				_ => grade.ToString(CultureInfo.InvariantCulture), 
			};
		}

		public bool Equals(Enhancement other)
		{
			if (Gem == other.Gem)
			{
				return Grade == other.Grade;
			}
			return false;
		}

		public override bool Equals(object obj)
		{
			if (obj is Enhancement other)
			{
				return Equals(other);
			}
			return false;
		}

		public override int GetHashCode()
		{
			return ((int)Gem * 397) ^ Grade;
		}

		public override string ToString()
		{
			if (!IsEmpty)
			{
				return $"{Gem} {Roman(Grade)}";
			}
			return "none";
		}
	}
	public sealed class EnhanceSettings
	{
		public sealed class Field
		{
			public string Section { get; }

			public string Name { get; }

			public float Default { get; }

			public float Min { get; }

			public float Max { get; }

			public string Description { get; }

			public Field(string section, string name, float defaultValue, float min, float max, string description)
			{
				Section = section;
				Name = name;
				Default = defaultValue;
				Min = min;
				Max = max;
				Description = description;
			}
		}

		public sealed class TextField
		{
			public string Section { get; }

			public string Name { get; }

			public string Default { get; }

			public string Description { get; }

			public TextField(string section, string name, string defaultValue, string description)
			{
				Section = section;
				Name = name;
				Default = defaultValue;
				Description = description;
			}
		}

		public const string CostsSection = "1 - Costs";

		public const string EffectsSection = "2 - Gem effects";

		public const string GeneralSection = "3 - General";

		public const string EffectHelp = "Comma separated Effect:value-per-grade (grade III = 3x). The first entry is the main benefit, which ArmorCaps measure. Effects: MaxHealth, HealthRegen, StaminaRegen, EitrRegen, PhysicalDamageTaken, MoveSpeed, CarryWeight (flat), RunStamina, Durability, AttackStamina, EitrCost, LifeSteal, Damage. Values in % with their natural sign: StaminaRegen:2 = +2%, AttackStamina:-3 = 3% less stamina per attack, PhysicalDamageTaken:-1 = 1% less damage taken. Durability and Damage change the item itself. Empty = the gem does nothing in that slot.";

		public static readonly IReadOnlyList<Field> Fields = new Field[8]
		{
			new Field("1 - Costs", "MaxGrade", 3f, 1f, 3f, "Highest grade a gem can be polished to (1 = no polishing)."),
			new Field("1 - Costs", "SetCoins", 20f, 0f, 10000f, "Ancient Coins to set a gem (grade I)."),
			new Field("1 - Costs", "PolishToIICoins", 60f, 0f, 10000f, "Ancient Coins to polish a gem to grade II."),
			new Field("1 - Costs", "PolishToIIICoins", 150f, 0f, 10000f, "Ancient Coins to polish a gem to grade III."),
			new Field("1 - Costs", "RemoveCoins", 30f, 0f, 10000f, "Ancient Coins to remove a gem."),
			new Field("1 - Costs", "SetGems", 1f, 1f, 20f, "Gems used to set a gem."),
			new Field("1 - Costs", "PolishToIIGems", 1f, 0f, 20f, "Extra gems of the same kind used to polish to grade II."),
			new Field("1 - Costs", "PolishToIIIGems", 2f, 0f, 20f, "Extra gems of the same kind used to polish to grade III.")
		};

		public static readonly IReadOnlyList<TextField> TextFields = new TextField[11]
		{
			new TextField("2 - Gem effects", "GrimvarnOnArmor", "MaxHealth:1, StaminaRegen:-1", "Grimvarn set in armor. Comma separated Effect:value-per-grade (grade III = 3x). The first entry is the main benefit, which ArmorCaps measure. Effects: MaxHealth, HealthRegen, StaminaRegen, EitrRegen, PhysicalDamageTaken, MoveSpeed, CarryWeight (flat), RunStamina, Durability, AttackStamina, EitrCost, LifeSteal, Damage. Values in % with their natural sign: StaminaRegen:2 = +2%, AttackStamina:-3 = 3% less stamina per attack, PhysicalDamageTaken:-1 = 1% less damage taken. Durability and Damage change the item itself. Empty = the gem does nothing in that slot."),
			new TextField("2 - Gem effects", "SolrythOnArmor", "StaminaRegen:2, MaxHealth:-0.5", "Solryth set in armor."),
			new TextField("2 - Gem effects", "VeydrisOnArmor", "EitrRegen:2, StaminaRegen:-1", "Veydris set in armor."),
			new TextField("2 - Gem effects", "DraumyxOnArmor", "PhysicalDamageTaken:-1, MoveSpeed:-0.5", "Draumyx set in armor."),
			new TextField("2 - Gem effects", "GrimvarnOnWeapon", "Durability:10, AttackStamina:1", "Grimvarn set in a weapon (works while it is in hand)."),
			new TextField("2 - Gem effects", "SolrythOnWeapon", "AttackStamina:-3, Durability:-5", "Solryth set in a weapon."),
			new TextField("2 - Gem effects", "VeydrisOnWeapon", "EitrCost:-3, Durability:-5", "Veydris set in a weapon."),
			new TextField("2 - Gem effects", "DraumyxOnWeapon", "LifeSteal:1, HealthRegen:-2", "Draumyx set in a weapon."),
			new TextField("2 - Gem effects", "ArmorCaps", "MaxHealth:10, StaminaRegen:15, EitrRegen:15, PhysicalDamageTaken:8", "Most a main benefit can give from all worn armor together, as Effect:percent (always the helpful amount, so PhysicalDamageTaken:8 = at most 8% less). When a cap cuts a gem's main benefit, its side effects are cut by the same share."),
			new TextField("3 - General", "Stations", "$piece_blackforge:max:3, $piece_magetable:max:2", "Where enhancing works, as station:stationLevel:gearLevel. The station must be upgraded to at least stationLevel; max = fully upgraded in vanilla (black forge 6, Galdr table 5; extensions from other mods do not raise it). Only gear whose recipe needs at least gearLevel at that station can take a gem (default: Ashlands gear and above). gearLevel defaults to 1 when left out."),
			new TextField("3 - General", "EnhanceableSlots", "Helmet,Chest,Legs,Cape,Shield,Weapon", "Which gear can hold a gem, comma separated: Helmet, Chest, Legs, Cape, Shield, Weapon. Shields and capes count as armor.")
		};

		public static readonly EnhanceSettings Default = new EnhanceSettings(enabled: true, removeReturnsGem: false, null, null);

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

		private readonly Dictionary<string, string> texts = new Dictionary<string, string>(StringComparer.Ordinal);

		private readonly Dictionary<(Gem, bool), EffectList> effects = new Dictionary<(Gem, bool), EffectList>();

		private readonly Dictionary<Effect, float> caps = new Dictionary<Effect, float>();

		private readonly Dictionary<string, (int Station, int Gear)> stations = new Dictionary<string, (int, int)>(StringComparer.Ordinal);

		private readonly HashSet<Slot> slots = new HashSet<Slot>();

		public const int MaxLevel = 0;

		public bool Enabled { get; }

		public bool RemoveReturnsGem { get; }

		public List<string> Errors { get; }

		public float this[string name] => values[name];

		public int MaxGrade => (int)Math.Round(this["MaxGrade"]);

		public EnhanceSettings(bool enabled, bool removeReturnsGem, IReadOnlyDictionary<string, float> numbers, IReadOnlyDictionary<string, string> textValues)
		{
			Enabled = enabled;
			RemoveReturnsGem = removeReturnsGem;
			foreach (Field field in Fields)
			{
				float value;
				float val = ((numbers != null && numbers.TryGetValue(field.Name, out value) && !float.IsNaN(value) && !float.IsInfinity(value)) ? value : field.Default);
				values[field.Name] = Math.Min(field.Max, Math.Max(field.Min, val));
			}
			foreach (TextField textField in TextFields)
			{
				string value2;
				string text = ((textValues != null && textValues.TryGetValue(textField.Name, out value2) && value2 != null) ? value2 : textField.Default);
				texts[textField.Name] = Sanitize(text);
			}
			Errors = new List<string>();
			Gem[] all = Gems.All;
			for (int i = 0; i < all.Length; i++)
			{
				Gem item = all[i];
				effects[(item, true)] = EffectList.Parse(texts[item.ToString() + "OnArmor"], Errors);
				effects[(item, false)] = EffectList.Parse(texts[item.ToString() + "OnWeapon"], Errors);
			}
			foreach (var (key, value3) in EffectList.Parse(texts["ArmorCaps"], Errors).Entries)
			{
				caps[key] = Math.Abs(value3);
			}
			string[] array = texts["Stations"].Split(new char[1] { ',' });
			for (int i = 0; i < array.Length; i++)
			{
				string[] array2 = array[i].Split(new char[1] { ':' });
				string text2 = array2[0].Trim();
				if (text2.Length > 0)
				{
					bool flag = array2.Length > 1 && array2[1].Trim().Equals("max", StringComparison.OrdinalIgnoreCase);
					stations[text2] = ((!flag) ? Level(array2, 1) : 0, Level(array2, 2));
				}
			}
			array = texts["EnhanceableSlots"].Split(new char[1] { ',' });
			for (int i = 0; i < array.Length; i++)
			{
				if (Enum.TryParse<Slot>(array[i].Trim(), ignoreCase: true, out var result) && result != Slot.None)
				{
					slots.Add(result);
				}
			}
		}

		public string Text(string name)
		{
			return texts[name];
		}

		public bool CanEnhance(Slot slot)
		{
			if (Enabled && slot != Slot.None)
			{
				return slots.Contains(slot);
			}
			return false;
		}

		public EffectList EffectsOf(Gem gem, Slot slot)
		{
			if (gem == Gem.None || slot == Slot.None)
			{
				return EffectList.Empty;
			}
			return effects[(gem, Rules.IsArmor(slot))];
		}

		public float? CapOf(Effect effect)
		{
			if (!caps.TryGetValue(effect, out var value))
			{
				return null;
			}
			return value;
		}

		public static int? VanillaMaxLevel(string stationName)
		{
			return stationName switch
			{
				"$piece_blackforge" => 6, 
				"$piece_magetable" => 5, 
				"$piece_forge" => 7, 
				"$piece_workbench" => 5, 
				"$piece_cauldron" => 7, 
				"$piece_artisanstation" => 2, 
				_ => null, 
			};
		}

		public (int Station, int Gear)? StationRequirement(string stationName)
		{
			if (stationName == null || !stations.TryGetValue(stationName, out (int, int) value))
			{
				return null;
			}
			return value;
		}

		private static int Level(string[] pieces, int index)
		{
			if (pieces.Length <= index || !int.TryParse(pieces[index].Trim(), NumberStyles.Integer, CultureInfo.InvariantCulture, out var result))
			{
				return 1;
			}
			return Math.Max(1, result);
		}

		public string Encode()
		{
			List<string> list = new List<string>
			{
				Enabled ? "1" : "0",
				RemoveReturnsGem ? "1" : "0"
			};
			list.AddRange(Fields.Select((Field f) => f.Name + "=" + values[f.Name].ToString("R", CultureInfo.InvariantCulture)));
			list.AddRange(TextFields.Select((TextField f) => f.Name + "=" + texts[f.Name]));
			return string.Join(";", list);
		}

		public static bool TryDecode(string text, out EnhanceSettings settings)
		{
			settings = null;
			string[] array = (text ?? "").Split(new char[1] { ';' });
			if (array.Length < 2 || !IsBool(array[0]) || !IsBool(array[1]))
			{
				return false;
			}
			HashSet<string> hashSet = new HashSet<string>(Fields.Select((Field f) => f.Name));
			Dictionary<string, float> dictionary = new Dictionary<string, float>(StringComparer.Ordinal);
			Dictionary<string, string> dictionary2 = new Dictionary<string, string>(StringComparer.Ordinal);
			for (int num = 2; num < array.Length; num++)
			{
				int num2 = array[num].IndexOf('=');
				if (num2 <= 0)
				{
					return false;
				}
				string text2 = array[num].Substring(0, num2);
				string text3 = array[num].Substring(num2 + 1);
				if (hashSet.Contains(text2))
				{
					if (!float.TryParse(text3, NumberStyles.Float, CultureInfo.InvariantCulture, out var result))
					{
						return false;
					}
					dictionary[text2] = result;
				}
				else
				{
					dictionary2[text2] = text3;
				}
			}
			settings = new EnhanceSettings(array[0] == "1", array[1] == "1", dictionary, dictionary2);
			return true;
		}

		private static string Sanitize(string text)
		{
			return text.Replace(";", ",").Replace("=", ":").Trim();
		}

		private static bool IsBool(string s)
		{
			if (!(s == "0"))
			{
				return s == "1";
			}
			return true;
		}
	}
	public enum Gem
	{
		None,
		Grimvarn,
		Solryth,
		Veydris,
		Draumyx
	}
	public static class Gems
	{
		public static readonly Gem[] All = new Gem[4]
		{
			Gem.Grimvarn,
			Gem.Solryth,
			Gem.Veydris,
			Gem.Draumyx
		};

		public const string AncientCoinPrefab = "AncientCoin";

		public static string PrefabName(Gem gem)
		{
			return gem switch
			{
				Gem.Grimvarn => "AncientGemstoneGreen", 
				Gem.Solryth => "AncientGemstoneOrange", 
				Gem.Veydris => "AncientGemstonePurple", 
				Gem.Draumyx => "AncientGemstoneBlack", 
				_ => throw new ArgumentOutOfRangeException("gem", gem, null), 
			};
		}

		public static Gem Parse(string text)
		{
			if (!Enum.TryParse<Gem>(text, ignoreCase: true, out var result) || Array.IndexOf(All, result) < 0)
			{
				return Gem.None;
			}
			return result;
		}
	}
	public enum Slot
	{
		None,
		Helmet,
		Chest,
		Legs,
		Cape,
		Shield,
		Weapon
	}
	public enum EnhanceAction
	{
		Set,
		Polish,
		Remove
	}
	public readonly struct Cost
	{
		public int Gems { get; }

		public int Coins { get; }

		public Cost(int gems, int coins)
		{
			Gems = gems;
			Coins = coins;
		}
	}
	public sealed class EffectTotals
	{
		public static readonly EffectTotals None = new EffectTotals(new Dictionary<Effect, float>());

		private readonly Dictionary<Effect, float> totals;

		public float this[Effect effect]
		{
			get
			{
				if (!totals.TryGetValue(effect, out var value))
				{
					return 0f;
				}
				return value;
			}
		}

		public EffectTotals(Dictionary<Effect, float> totals)
		{
			this.totals = totals;
		}
	}
	public static class Rules
	{
		public static Slot SlotFor(int itemType)
		{
			switch (itemType)
			{
			case 3:
			case 4:
			case 14:
			case 22:
				return Slot.Weapon;
			case 5:
				return Slot.Shield;
			case 6:
				return Slot.Helmet;
			case 7:
				return Slot.Chest;
			case 11:
				return Slot.Legs;
			case 17:
				return Slot.Cape;
			default:
				return Slot.None;
			}
		}

		public static bool IsArmor(Slot slot)
		{
			if (slot != Slot.None)
			{
				return slot != Slot.Weapon;
			}
			return false;
		}

		public static bool CanDo(EnhanceAction action, Enhancement current, EnhanceSettings settings)
		{
			switch (action)
			{
			case EnhanceAction.Set:
				return current.IsEmpty;
			case EnhanceAction.Polish:
				if (!current.IsEmpty)
				{
					return current.Grade < settings.MaxGrade;
				}
				return false;
			case EnhanceAction.Remove:
				return !current.IsEmpty;
			default:
				return false;
			}
		}

		public static Cost CostOf(EnhanceAction action, Enhancement current, EnhanceSettings settings)
		{
			switch (action)
			{
			case EnhanceAction.Set:
				return new Cost(Int(settings["SetGems"]), Int(settings["SetCoins"]));
			case EnhanceAction.Polish:
				if (current.Grade + 1 < 3)
				{
					return new Cost(Int(settings["PolishToIIGems"]), Int(settings["PolishToIICoins"]));
				}
				return new Cost(Int(settings["PolishToIIIGems"]), Int(settings["PolishToIIICoins"]));
			case EnhanceAction.Remove:
				return new Cost(0, Int(settings["RemoveCoins"]));
			default:
				throw new ArgumentOutOfRangeException("action", action, null);
			}
		}

		public static Enhancement Apply(EnhanceAction action, Enhancement current, Gem chosen)
		{
			return action switch
			{
				EnhanceAction.Set => new Enhancement(chosen, 1), 
				EnhanceAction.Polish => new Enhancement(current.Gem, current.Grade + 1), 
				EnhanceAction.Remove => Enhancement.Empty, 
				_ => throw new ArgumentOutOfRangeException("action", action, null), 
			};
		}

		public static IEnumerable<(Effect Effect, float Value)> ItemEffects(Enhancement e, Slot slot, EnhanceSettings s)
		{
			if (e.IsEmpty || !s.CanEnhance(slot))
			{
				yield break;
			}
			int grade = Math.Min(e.Grade, s.MaxGrade);
			foreach (var (item, num) in s.EffectsOf(e.Gem, slot).Entries)
			{
				yield return (Effect: item, Value: num * (float)grade);
			}
		}

		public static float ItemOwnEffect(Enhancement e, Slot slot, Effect effect, EnhanceSettings s)
		{
			float num = 0f;
			foreach (var (effect2, num2) in ItemEffects(e, slot, s))
			{
				if (effect2 == effect)
				{
					num += num2;
				}
			}
			return num;
		}

		public static EffectTotals PlayerTotals(IEnumerable<(Enhancement Enhancement, Slot Slot)> worn, Enhancement weapon, EnhanceSettings s)
		{
			Dictionary<Effect, float> dictionary = new Dictionary<Effect, float>();
			if (!s.Enabled)
			{
				return new EffectTotals(dictionary);
			}
			List<(Effect, List<(Effect, float)>)> list = new List<(Effect, List<(Effect, float)>)>();
			Dictionary<Effect, float> dictionary2 = new Dictionary<Effect, float>();
			foreach (var (e, slot) in worn)
			{
				if (IsArmor(slot))
				{
					List<(Effect, float)> list2 = new List<(Effect, float)>(ItemEffects(e, slot, s));
					if (list2.Count != 0)
					{
						Effect item = list2[0].Item1;
						list.Add((item, list2));
						dictionary2[item] = (dictionary2.TryGetValue(item, out var value) ? value : 0f) + Math.Max(0f, Effects.Goodness(item, list2[0].Item2));
					}
				}
			}
			foreach (var item4 in list)
			{
				Effect item2 = item4.Item1;
				List<(Effect, float)> item3 = item4.Item2;
				float? num = s.CapOf(item2);
				float num2 = dictionary2[item2];
				float num3 = ((num.HasValue && num2 > num.Value) ? (num.Value / num2) : 1f);
				foreach (var (effect, num4) in item3)
				{
					if (!Effects.OnItem(effect))
					{
						dictionary[effect] = (dictionary.TryGetValue(effect, out var value2) ? value2 : 0f) + num4 * num3;
					}
				}
			}
			foreach (var (effect2, num5) in ItemEffects(weapon, Slot.Weapon, s))
			{
				if (!Effects.OnItem(effect2))
				{
					dictionary[effect2] = (dictionary.TryGetValue(effect2, out var value3) ? value3 : 0f) + num5;
				}
			}
			return new EffectTotals(dictionary);
		}

		private static int Int(float value)
		{
			return (int)Math.Round(value);
		}
	}
}

plugins/NorthernGemcraft.dll

Decompiled 3 hours ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Text;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using NorthernGemcraft.Core;
using TMPro;
using UnityEngine;
using UnityEngine.Events;
using UnityEngine.UI;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETFramework,Version=v4.7.2", FrameworkDisplayName = ".NET Framework 4.7.2")]
[assembly: AssemblyCompany("jonasd")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("0.1.2.0")]
[assembly: AssemblyInformationalVersion("0.1.2+fb61e20309b17b5c1cca7e2cd23a964eea5e02c8")]
[assembly: AssemblyProduct("Northern Gemcraft")]
[assembly: AssemblyTitle("NorthernGemcraft")]
[assembly: AssemblyVersion("0.1.2.0")]
namespace NorthernGemcraft
{
	internal static class Discovery
	{
		[HarmonyPatch(typeof(Player), "AddKnownItem")]
		private static class PickupPatch
		{
			private static void Postfix(Player __instance)
			{
				Check(__instance);
			}
		}

		private const string DiscoveredKey = "northerngemcraft.discovered.";

		private static string lastReport;

		public static bool Knows(Player player, string prefabName)
		{
			ItemDrop val = Enhancer.PrefabItem(prefabName);
			if ((Object)(object)val != (Object)null)
			{
				return player.IsKnownMaterial(val.m_itemData.m_shared.m_name);
			}
			return false;
		}

		public unsafe static bool IsDiscovered(Player player, Gem gem)
		{
			if ((Object)(object)player != (Object)null)
			{
				if (!player.NoCostCheat())
				{
					return player.m_customData.ContainsKey("northerngemcraft.discovered." + ((object)(*(Gem*)(&gem))/*cast due to .constrained prefix*/).ToString());
				}
				return true;
			}
			return false;
		}

		public static bool AnyDiscovered(Player player)
		{
			//IL_000d: 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)
			Gem[] all = Gems.All;
			foreach (Gem gem in all)
			{
				if (IsDiscovered(player, gem))
				{
					return true;
				}
			}
			return false;
		}

		public unsafe static void Check(Player player)
		{
			//IL_0061: Unknown result type (might be due to invalid IL or missing references)
			//IL_0089: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e5: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)player == (Object)null || (Object)(object)player != (Object)(object)Player.m_localPlayer || (Object)(object)MessageHud.instance == (Object)null)
			{
				return;
			}
			Report(player);
			if (!SettingsSync.Current.Enabled || !Eligibility.StationReady(player.GetCurrentCraftingStation()) || !Knows(player, "AncientCoin"))
			{
				return;
			}
			Gem[] all = Gems.All;
			for (int i = 0; i < all.Length; i++)
			{
				Gem val = all[i];
				string key = "northerngemcraft.discovered." + ((object)(*(Gem*)(&val))/*cast due to .constrained prefix*/).ToString();
				if (!player.m_customData.ContainsKey(key) && Knows(player, Gems.PrefabName(val)))
				{
					player.m_customData[key] = "1";
					ItemDrop val2 = Enhancer.PrefabItem(Gems.PrefabName(val));
					MessageHud.instance.QueueUnlockMsg(val2.m_itemData.GetIcon(), "Discovered: Enhancing", val2.m_itemData.m_shared.m_name);
					Plugin.Detail($"Discovered enhancing with {val}");
				}
			}
		}

		private static void Report(Player player)
		{
			if (Plugin.LogDetails != null && Plugin.LogDetails.Value)
			{
				CraftingStation currentCraftingStation = player.GetCurrentCraftingStation();
				string arg = string.Join(", ", Array.ConvertAll(Gems.All, (Gem g) => $"{g} known={Knows(player, Gems.PrefabName(g))} discovered={IsDiscovered(player, g)}"));
				string text = (((Object)(object)currentCraftingStation == (Object)null) ? "no station" : (string.Format("station {0} level {1} (needs {2}, ", currentCraftingStation.m_name, currentCraftingStation.GetLevel(true), Eligibility.RequiredLevel(currentCraftingStation)?.ToString() ?? "not allowed") + string.Format("craft tab {0}); coin known={1}; {2}", currentCraftingStation.m_hasCraftTab, Knows(player, "AncientCoin"), arg)));
				if (text != lastReport)
				{
					lastReport = text;
					Plugin.Log.LogInfo((object)("[enhance check] " + text));
				}
			}
		}
	}
	internal static class Eligibility
	{
		private static readonly Dictionary<string, int> MaxLevels = new Dictionary<string, int>();

		public static int? RequiredLevel(CraftingStation station)
		{
			int? num = (((Object)(object)station == (Object)null) ? ((int?)null) : SettingsSync.Current.StationRequirement(station.m_name)?.Item1);
			if (num != 0)
			{
				return num;
			}
			return MaxLevelOf(station);
		}

		public static int MaxLevelOf(CraftingStation station)
		{
			int? num = EnhanceSettings.VanillaMaxLevel(station.m_name);
			if (num.HasValue)
			{
				return num.Value;
			}
			if (MaxLevels.TryGetValue(station.m_name, out var value))
			{
				return value;
			}
			int num2 = 0;
			if ((Object)(object)ZNetScene.instance != (Object)null)
			{
				foreach (GameObject prefab in ZNetScene.instance.m_prefabs)
				{
					StationExtension val = (((Object)(object)prefab != (Object)null) ? prefab.GetComponent<StationExtension>() : null);
					if ((Object)(object)val != (Object)null && (Object)(object)val.m_craftingStation != (Object)null && val.m_craftingStation.m_name == station.m_name)
					{
						num2++;
					}
				}
			}
			MaxLevels[station.m_name] = 1 + num2;
			Plugin.Detail($"Fully upgraded {station.m_name} = level {1 + num2}");
			return 1 + num2;
		}

		public static bool StationReady(CraftingStation station)
		{
			int? num = RequiredLevel(station);
			if (num.HasValue)
			{
				return station.GetLevel(true) >= num.Value;
			}
			return false;
		}

		public static bool RecipeQualifies(Recipe recipe, CraftingStation station)
		{
			//IL_0086: Unknown result type (might be due to invalid IL or missing references)
			//IL_0090: Expected I4, but got Unknown
			//IL_008b: Unknown result type (might be due to invalid IL or missing references)
			(int, int)? tuple = (((Object)(object)station == (Object)null) ? (((int, int)?)null) : SettingsSync.Current.StationRequirement(station.m_name));
			if ((Object)(object)recipe != (Object)null && (Object)(object)recipe.m_craftingStation != (Object)null && tuple.HasValue && recipe.m_craftingStation.m_name == station.m_name && recipe.m_minStationLevel >= tuple.Value.Item2)
			{
				return SettingsSync.Current.CanEnhance(Rules.SlotFor((int)recipe.m_item.m_itemData.m_shared.m_itemType));
			}
			return false;
		}

		public static bool ItemQualifies(ItemData item, CraftingStation station)
		{
			if (item != null && (Object)(object)ObjectDB.instance != (Object)null)
			{
				return RecipeQualifies(ObjectDB.instance.GetRecipe(item), station);
			}
			return false;
		}
	}
	internal static class Enhancements
	{
		private static readonly List<(Enhancement, Slot)> Worn = new List<(Enhancement, Slot)>();

		private static int totalsFrame = -1;

		private static EffectTotals totals = EffectTotals.None;

		public static EffectTotals Local
		{
			get
			{
				//IL_0063: 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_0078: Unknown result type (might be due to invalid IL or missing references)
				//IL_007c: Unknown result type (might be due to invalid IL or missing references)
				//IL_00ac: Unknown result type (might be due to invalid IL or missing references)
				//IL_00b2: Invalid comparison between Unknown and I4
				//IL_00bc: Unknown result type (might be due to invalid IL or missing references)
				//IL_00b4: Unknown result type (might be due to invalid IL or missing references)
				//IL_00c1: 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)
				if (totalsFrame == Time.frameCount)
				{
					return totals;
				}
				totalsFrame = Time.frameCount;
				Worn.Clear();
				Player localPlayer = Player.m_localPlayer;
				if ((Object)(object)localPlayer == (Object)null)
				{
					totals = EffectTotals.None;
					return totals;
				}
				foreach (ItemData equippedItem in ((Humanoid)localPlayer).GetInventory().GetEquippedItems())
				{
					Enhancement item = Of(equippedItem);
					if (!((Enhancement)(ref item)).IsEmpty)
					{
						Worn.Add((item, SlotOf(equippedItem)));
					}
				}
				ItemData currentWeapon = ((Humanoid)localPlayer).GetCurrentWeapon();
				Enhancement val = (((int)SlotOf(currentWeapon) == 6) ? Of(currentWeapon) : Enhancement.Empty);
				totals = Rules.PlayerTotals((IEnumerable<ValueTuple<Enhancement, Slot>>)Worn, val, SettingsSync.Current);
				return totals;
			}
		}

		public static Enhancement Of(ItemData item)
		{
			//IL_000f: 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)
			if (item != null)
			{
				return Enhancement.Read((IReadOnlyDictionary<string, string>)item.m_customData);
			}
			return Enhancement.Empty;
		}

		public static Slot SlotOf(ItemData item)
		{
			//IL_0009: Unknown result type (might be due to invalid IL or missing references)
			//IL_0013: Expected I4, but got Unknown
			//IL_000e: Unknown result type (might be due to invalid IL or missing references)
			if (item != null)
			{
				return Rules.SlotFor((int)item.m_shared.m_itemType);
			}
			return (Slot)0;
		}

		public static void Write(ItemData item, Enhancement enhancement)
		{
			((Enhancement)(ref enhancement)).Write((IDictionary<string, string>)item.m_customData);
		}

		public static bool IsLocalPlayer(Character character)
		{
			if ((Object)(object)character != (Object)null)
			{
				return (Object)(object)character == (Object)(object)Player.m_localPlayer;
			}
			return false;
		}

		public static float OwnEffect(ItemData item, Effect effect)
		{
			//IL_0004: 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)
			if (item != null)
			{
				return Rules.ItemOwnEffect(Of(item), SlotOf(item), effect, SettingsSync.Current);
			}
			return 0f;
		}

		public unsafe static string GemName(Gem gem)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			ItemDrop val = Enhancer.PrefabItem(Gems.PrefabName(gem));
			if (!((Object)(object)val != (Object)null))
			{
				return ((object)(*(Gem*)(&gem))/*cast due to .constrained prefix*/).ToString();
			}
			return Localization.instance.Localize(val.m_itemData.m_shared.m_name);
		}

		public static string EffectText(Gem gem, Slot slot, int grade)
		{
			//IL_0000: 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)
			IEnumerable<string> values = from fx in Rules.ItemEffects(new Enhancement(gem, grade), slot, SettingsSync.Current)
				select "<color=" + ((Effects.Goodness(fx.Effect, fx.Value) >= 0f) ? "#9EE07A" : "#E08A7A") + ">" + Effects.Format(fx.Effect, fx.Value) + "</color>";
			string text = string.Join(", ", values);
			if (text.Length <= 0)
			{
				return "no effect";
			}
			return text;
		}
	}
	internal static class Enhancer
	{
		private static readonly MethodInfo HaveRequirementItems = AccessTools.Method(typeof(Player), "HaveRequirementItems", new Type[4]
		{
			typeof(Recipe),
			typeof(bool),
			typeof(int),
			typeof(int)
		}, (Type[])null);

		private static Recipe costRecipe;

		public static ItemDrop PrefabItem(string prefabName)
		{
			GameObject val = (((Object)(object)ObjectDB.instance != (Object)null) ? ObjectDB.instance.GetItemPrefab(prefabName) : null);
			if (!((Object)(object)val != (Object)null))
			{
				return null;
			}
			return val.GetComponent<ItemDrop>();
		}

		public static Gem GemFor(EnhanceAction action, Enhancement current, Gem chosen)
		{
			//IL_0000: 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_0005: Unknown result type (might be due to invalid IL or missing references)
			if ((int)action != 0)
			{
				return ((Enhancement)(ref current)).Gem;
			}
			return chosen;
		}

		public static bool CanAfford(Player player, EnhanceAction action, Enhancement current, Gem chosen)
		{
			//IL_0035: 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)
			//IL_0037: Unknown result type (might be due to invalid IL or missing references)
			if (player.NoCostCheat())
			{
				return true;
			}
			if ((Object)(object)costRecipe == (Object)null)
			{
				costRecipe = ScriptableObject.CreateInstance<Recipe>();
				((Object)costRecipe).name = "NorthernGemcraftCost";
			}
			costRecipe.m_resources = Requirements(action, current, chosen);
			return (bool)HaveRequirementItems.Invoke(player, new object[4] { costRecipe, false, 1, 1 });
		}

		public static Requirement[] Requirements(EnhanceAction action, Enhancement current, Gem chosen)
		{
			//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_0007: Unknown result type (might be due to invalid IL or missing references)
			//IL_000c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0014: Unknown result type (might be due to invalid IL or missing references)
			//IL_0015: Unknown result type (might be due to invalid IL or missing references)
			//IL_0016: Unknown result type (might be due to invalid IL or missing references)
			//IL_0017: Unknown result type (might be due to invalid IL or missing references)
			Cost val = Rules.CostOf(action, current, SettingsSync.Current);
			List<Requirement> list = new List<Requirement>(2);
			Add(list, Gems.PrefabName(GemFor(action, current, chosen)), ((Cost)(ref val)).Gems);
			Add(list, "AncientCoin", ((Cost)(ref val)).Coins);
			return list.ToArray();
		}

		private static void Add(List<Requirement> requirements, string prefabName, int amount)
		{
			//IL_0015: 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_0021: 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_002f: Unknown result type (might be due to invalid IL or missing references)
			//IL_003b: Expected O, but got Unknown
			ItemDrop val = PrefabItem(prefabName);
			if ((Object)(object)val != (Object)null && amount > 0)
			{
				requirements.Add(new Requirement
				{
					m_resItem = val,
					m_amount = amount,
					m_amountPerLevel = 0,
					m_recover = false
				});
			}
		}

		public static bool TryPerform(Player player, ItemData item, EnhanceAction action, Gem chosen)
		{
			//IL_000e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0013: Unknown result type (might be due to invalid IL or missing references)
			//IL_0016: 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_0052: Unknown result type (might be due to invalid IL or missing references)
			//IL_0053: Unknown result type (might be due to invalid IL or missing references)
			//IL_004a: Unknown result type (might be due to invalid IL or missing references)
			//IL_005d: Unknown result type (might be due to invalid IL or missing references)
			//IL_005e: Unknown result type (might be due to invalid IL or missing references)
			//IL_005f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0069: Unknown result type (might be due to invalid IL or missing references)
			//IL_006a: Unknown result type (might be due to invalid IL or missing references)
			//IL_006b: Unknown result type (might be due to invalid IL or missing references)
			//IL_006c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0071: Unknown result type (might be due to invalid IL or missing references)
			//IL_008b: Unknown result type (might be due to invalid IL or missing references)
			//IL_008c: Unknown result type (might be due to invalid IL or missing references)
			//IL_008d: Unknown result type (might be due to invalid IL or missing references)
			//IL_008e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0093: Unknown result type (might be due to invalid IL or missing references)
			//IL_0096: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b6: Invalid comparison between Unknown and I4
			//IL_007b: Unknown result type (might be due to invalid IL or missing references)
			//IL_007c: Unknown result type (might be due to invalid IL or missing references)
			//IL_007d: 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_00fd: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ff: Invalid comparison between Unknown and I4
			//IL_00c1: Unknown result type (might be due to invalid IL or missing references)
			//IL_0160: Unknown result type (might be due to invalid IL or missing references)
			//IL_0177: Unknown result type (might be due to invalid IL or missing references)
			//IL_0180: Unknown result type (might be due to invalid IL or missing references)
			EnhanceSettings current = SettingsSync.Current;
			Inventory inventory = ((Humanoid)player).GetInventory();
			Enhancement val = Enhancements.Of(item);
			if (!current.CanEnhance(Enhancements.SlotOf(item)) || !inventory.ContainsItem(item) || !Eligibility.StationReady(player.GetCurrentCraftingStation()) || !Eligibility.ItemQualifies(item, player.GetCurrentCraftingStation()) || ((int)action == 0 && !Discovery.IsDiscovered(player, chosen)) || !Rules.CanDo(action, val, current) || !CanAfford(player, action, val, chosen))
			{
				return false;
			}
			Gem val2 = GemFor(action, val, chosen);
			if (!player.NoCostCheat())
			{
				player.ConsumeResources(Requirements(action, val, chosen), 1, -1, 1);
			}
			Enhancement val3 = Rules.Apply(action, val, chosen);
			Enhancements.Write(item, val3);
			item.m_durability = Mathf.Min(item.m_durability, item.GetMaxDurability());
			if ((int)action == 2 && current.RemoveReturnsGem)
			{
				inventory.AddItem(Gems.PrefabName(val2), 1, 1, 0, player.GetPlayerID(), player.GetPlayerName(), false, false);
			}
			string text = Localization.instance.Localize(item.m_shared.m_name);
			string text2 = Enhancements.GemName(val2);
			string text3 = (((int)action == 2) ? (text2 + " removed from " + text) : (text + ": " + text2 + " " + Enhancement.Roman(((Enhancement)(ref val3)).Grade)));
			((Character)player).Message((MessageType)2, text3, 0, (Sprite)null, false);
			Plugin.Detail($"{action} on {item.m_shared.m_name}: {val} -> {val3}");
			return true;
		}
	}
	[BepInPlugin("com.jonasd.valheim.northerngemcraft", "Northern Gemcraft", "0.1.2")]
	public class Plugin : BaseUnityPlugin
	{
		public const string PluginGuid = "com.jonasd.valheim.northerngemcraft";

		public const string PluginName = "Northern Gemcraft";

		public const string PluginVersion = "0.1.2";

		internal static ManualLogSource Log;

		internal static ConfigEntry<bool> LogDetails;

		internal static ConfigEntry<bool> RequireOnClients;

		private readonly Dictionary<string, ConfigEntry<float>> numbers = new Dictionary<string, ConfigEntry<float>>();

		private readonly Dictionary<string, ConfigEntry<string>> texts = new Dictionary<string, ConfigEntry<string>>();

		private ConfigEntry<bool> enabledSetting;

		private ConfigEntry<bool> removeReturnsGem;

		private Harmony harmony;

		private void Awake()
		{
			//IL_01a3: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ad: Expected O, but got Unknown
			Log = ((BaseUnityPlugin)this).Logger;
			EnhanceSettings val = EnhanceSettings.Default;
			foreach (Field field in EnhanceSettings.Fields)
			{
				numbers[field.Name] = ((BaseUnityPlugin)this).Config.Bind<float>(field.Section, field.Name, field.Default, $"{field.Description} Range {field.Min:0.##} to {field.Max:0.##}.");
			}
			removeReturnsGem = ((BaseUnityPlugin)this).Config.Bind<bool>("1 - Costs", "RemoveReturnsGem", val.RemoveReturnsGem, "true = removing a gem gives it back (one gem, whatever its grade). false = the gem is destroyed.");
			foreach (TextField textField in EnhanceSettings.TextFields)
			{
				texts[textField.Name] = ((BaseUnityPlugin)this).Config.Bind<string>(textField.Section, textField.Name, textField.Default, textField.Description);
			}
			enabledSetting = ((BaseUnityPlugin)this).Config.Bind<bool>("3 - General", "Enabled", val.Enabled, "Turns enhancing and all gem effects on or off. On a server, the server's settings (all except LogDetails) apply to every player.");
			RequireOnClients = ((BaseUnityPlugin)this).Config.Bind<bool>("3 - General", "RequireOnClients", true, "Server only. true = players without this mod (or with another version) cannot join; they get the game's \"Incompatible version\" error.");
			LogDetails = ((BaseUnityPlugin)this).Config.Bind<bool>("3 - General", "LogDetails", false, "Write enhancements to the BepInEx log. For testing.");
			((BaseUnityPlugin)this).Config.SettingChanged += delegate
			{
				SettingsSync.OnLocalSettingsChanged(ReadLocalSettings());
			};
			SettingsSync.Initialize(ReadLocalSettings());
			harmony = new Harmony("com.jonasd.valheim.northerngemcraft");
			harmony.PatchAll();
		}

		private EnhanceSettings ReadLocalSettings()
		{
			//IL_00b6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bb: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c1: Expected O, but got Unknown
			//IL_00c2: Expected O, but got Unknown
			Dictionary<string, float> dictionary = new Dictionary<string, float>();
			foreach (KeyValuePair<string, ConfigEntry<float>> number in numbers)
			{
				dictionary[number.Key] = number.Value.Value;
			}
			Dictionary<string, string> dictionary2 = new Dictionary<string, string>();
			foreach (KeyValuePair<string, ConfigEntry<string>> text in texts)
			{
				dictionary2[text.Key] = text.Value.Value;
			}
			EnhanceSettings val = new EnhanceSettings(enabledSetting.Value, removeReturnsGem.Value, (IReadOnlyDictionary<string, float>)dictionary, (IReadOnlyDictionary<string, string>)dictionary2);
			WarnAbout(val);
			return val;
		}

		internal static void WarnAbout(EnhanceSettings settings)
		{
			if (settings.Errors.Count > 0)
			{
				Log.LogWarning((object)("Ignored unreadable gem effect/cap entries (use Effect:number, e.g. MaxHealth:1): " + string.Join(" | ", settings.Errors)));
			}
		}

		internal static void Detail(string message)
		{
			if (LogDetails != null && LogDetails.Value)
			{
				Log.LogInfo((object)message);
			}
		}

		private void OnDestroy()
		{
			Harmony obj = harmony;
			if (obj != null)
			{
				obj.UnpatchSelf();
			}
		}
	}
	internal static class SettingsSync
	{
		[HarmonyPatch(typeof(ZNet), "Awake")]
		private static class ZNetAwakePatch
		{
			private static void Postfix(ZNet __instance)
			{
				fromServer = null;
				ZRoutedRpc.instance.Register<string>("NG_Settings", (Action<long, string>)OnSettings);
				if (__instance.IsServer())
				{
					ZRoutedRpc instance = ZRoutedRpc.instance;
					instance.m_onNewPeer = (Action<long>)Delegate.Combine(instance.m_onNewPeer, new Action<long>(SendTo));
				}
			}
		}

		private const string SettingsRpc = "NG_Settings";

		private static EnhanceSettings local = EnhanceSettings.Default;

		private static EnhanceSettings fromServer;

		public static EnhanceSettings Current => fromServer ?? local;

		public static void Initialize(EnhanceSettings settings)
		{
			local = settings;
		}

		public static void OnLocalSettingsChanged(EnhanceSettings settings)
		{
			local = settings;
			if ((Object)(object)ZNet.instance != (Object)null && ZNet.instance.IsServer() && ZRoutedRpc.instance != null)
			{
				SendTo(0L);
			}
		}

		private static void SendTo(long peer)
		{
			ZRoutedRpc.instance.InvokeRoutedRPC(peer, "NG_Settings", new object[1] { local.Encode() });
		}

		private static void OnSettings(long sender, string encoded)
		{
			ZNetPeer val = (((Object)(object)ZNet.instance != (Object)null && !ZNet.instance.IsServer()) ? ZNet.instance.GetServerPeer() : null);
			if (val != null && sender == val.m_uid)
			{
				EnhanceSettings val2 = default(EnhanceSettings);
				if (EnhanceSettings.TryDecode(encoded, ref val2))
				{
					fromServer = val2;
					Plugin.Detail("Using the server's settings: " + encoded);
				}
				else
				{
					Plugin.Log.LogWarning((object)("Ignoring unreadable settings from the server: " + encoded));
				}
			}
		}
	}
	internal static class VersionCheck
	{
		private sealed class Box
		{
			public string Version;
		}

		[HarmonyPatch(typeof(ZNet), "OnNewConnection")]
		private static class NewConnectionPatch
		{
			private static void Postfix(ZNet __instance, ZNetPeer peer)
			{
				if (__instance.IsServer())
				{
					peer.m_rpc.Register<string>("NorthernGemcraft_Version", (Action<ZRpc, string>)delegate(ZRpc rpc, string version)
					{
						Received.GetOrCreateValue(rpc).Version = version;
					});
				}
				else
				{
					peer.m_rpc.Invoke("NorthernGemcraft_Version", new object[1] { "0.1.2" });
				}
			}
		}

		[HarmonyPatch(typeof(ZNet), "RPC_PeerInfo")]
		private static class PeerInfoPatch
		{
			private static bool Prefix(ZNet __instance, ZRpc rpc)
			{
				if (!__instance.IsServer() || !Plugin.RequireOnClients.Value)
				{
					return true;
				}
				Box value;
				string text = (Received.TryGetValue(rpc, out value) ? value.Version : null);
				if (text == "0.1.2")
				{
					return true;
				}
				Plugin.Log.LogWarning((object)("Turned away " + rpc.GetSocket().GetHostName() + ": " + ((text == null) ? "no Northern Gemcraft" : ("Northern Gemcraft " + text)) + ", server runs 0.1.2."));
				rpc.Invoke("Error", new object[1] { 3 });
				return false;
			}
		}

		private const string VersionRpc = "NorthernGemcraft_Version";

		private const int ErrorVersion = 3;

		private static readonly ConditionalWeakTable<ZRpc, Box> Received = new ConditionalWeakTable<ZRpc, Box>();
	}
}
namespace NorthernGemcraft.Ui
{
	internal static class EnhanceTab
	{
		private sealed class Entry
		{
			public ItemData Item;

			public EnhanceAction Action;

			public Gem Gem;

			public GameObject Element;

			public bool SameAs(Entry other)
			{
				//IL_0012: 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_0020: Unknown result type (might be due to invalid IL or missing references)
				//IL_0026: Unknown result type (might be due to invalid IL or missing references)
				if (other != null && other.Item == Item && other.Action == Action)
				{
					return other.Gem == Gem;
				}
				return false;
			}
		}

		[HarmonyPatch(typeof(InventoryGui), "Show")]
		private static class ShowPatch
		{
			private static void Prefix(InventoryGui __instance)
			{
				if (Active)
				{
					SetActive(__instance, active: false);
					((Selectable)__instance.m_tabCraft).interactable = false;
				}
			}
		}

		[HarmonyPatch(typeof(InventoryGui), "OnTabCraftPressed")]
		private static class CraftTabPatch
		{
			private static void Prefix(InventoryGui __instance)
			{
				SetActive(__instance, active: false);
			}
		}

		[HarmonyPatch(typeof(InventoryGui), "OnTabUpgradePressed")]
		private static class UpgradeTabPatch
		{
			private static void Prefix(InventoryGui __instance)
			{
				SetActive(__instance, active: false);
			}
		}

		[HarmonyPatch(typeof(InventoryGui), "OnSelectedRecipe")]
		private static class SelectRowPatch
		{
			private static void Postfix(InventoryGui __instance, GameObject button)
			{
				if (Active && (Object)(object)button != (Object)null && Entries.TryGetValue(button, out var value))
				{
					Select(__instance, value);
				}
			}
		}

		[HarmonyPatch(typeof(InventoryGui), "UpdateCraftingPanel")]
		private static class PanelPatch
		{
			private static bool Available(Player player)
			{
				CraftingStation val = (((Object)(object)player != (Object)null) ? player.GetCurrentCraftingStation() : null);
				if (SettingsSync.Current.Enabled && (Object)(object)val != (Object)null && val.m_hasCraftTab && Eligibility.StationReady(val))
				{
					return Discovery.AnyDiscovered(player);
				}
				return false;
			}

			private static void Prefix(InventoryGui __instance)
			{
				if (Active)
				{
					if (!Available(Player.m_localPlayer))
					{
						SetActive(__instance, active: false);
						((Selectable)__instance.m_tabCraft).interactable = false;
					}
					else
					{
						((Selectable)__instance.m_tabCraft).interactable = true;
						((Selectable)__instance.m_tabUpgrade).interactable = true;
					}
				}
			}

			private static void Postfix(InventoryGui __instance)
			{
				Discovery.Check(Player.m_localPlayer);
				EnsureTab(__instance);
				((Component)tab).gameObject.SetActive(Available(Player.m_localPlayer) && ((Component)__instance.m_tabCraft).gameObject.activeSelf && ((Component)__instance.m_tabUpgrade).gameObject.activeSelf);
				if (Active)
				{
					Entry entry = SelectedEntry(__instance) ?? ((Ordered.Count > 0) ? Ordered[0] : null);
					if (entry != null)
					{
						Select(__instance, entry);
					}
				}
			}
		}

		[HarmonyPatch(typeof(InventoryGui), "UpdateRecipeList")]
		private static class ListPatch
		{
			private static bool Prefix(InventoryGui __instance, List<Recipe> recipes)
			{
				//IL_00fb: Unknown result type (might be due to invalid IL or missing references)
				//IL_0105: Expected I4, but got Unknown
				//IL_0100: Unknown result type (might be due to invalid IL or missing references)
				//IL_011f: Unknown result type (might be due to invalid IL or missing references)
				if (!Active)
				{
					return true;
				}
				IList list = AvailableRecipes.Invoke(__instance);
				foreach (object item in list)
				{
					GameObject val = ElementOf(item);
					if ((Object)(object)val != (Object)null)
					{
						Object.Destroy((Object)(object)val);
					}
				}
				list.Clear();
				Entries.Clear();
				Ordered.Clear();
				Entry previous = selected;
				selected = null;
				Player localPlayer = Player.m_localPlayer;
				listStation = localPlayer.GetCurrentCraftingStation();
				EnhanceSettings current = SettingsSync.Current;
				foreach (ItemData allItem in ((Humanoid)localPlayer).GetInventory().GetAllItems())
				{
					Recipe recipe = ObjectDB.instance.GetRecipe(allItem);
					if (Eligibility.RecipeQualifies(recipe, listStation))
					{
						AddEntries(__instance, localPlayer, recipe, allItem, current);
					}
					else if ((int)Rules.SlotFor((int)allItem.m_shared.m_itemType) != 0)
					{
						Plugin.Detail($"  skipped {allItem.m_shared.m_name} ({allItem.m_shared.m_itemType}): " + (((Object)(object)recipe == (Object)null) ? "no recipe" : string.Format("made at {0} level {1}", recipe.m_craftingStation?.m_name ?? "no station", recipe.m_minStationLevel)));
					}
				}
				Plugin.Detail($"Enhance list at {listStation?.m_name}: {Ordered.Count} rows");
				selected = Ordered.Find((Entry e) => e.SameAs(previous)) ?? ((previous != null) ? Ordered.Find((Entry e) => e.Item == previous.Item) : null);
				float num = Mathf.Max(ListBaseSize.Invoke(__instance), (float)list.Count * __instance.m_recipeListSpace);
				__instance.m_recipeListRoot.SetSizeWithCurrentAnchors((Axis)1, num);
				return false;
			}

			private static void AddEntries(InventoryGui gui, Player player, Recipe recipe, ItemData item, EnhanceSettings settings)
			{
				//IL_0001: Unknown result type (might be due to invalid IL or missing references)
				//IL_0006: Unknown result type (might be due to invalid IL or missing references)
				//IL_0055: Unknown result type (might be due to invalid IL or missing references)
				//IL_0098: Unknown result type (might be due to invalid IL or missing references)
				//IL_00a0: Unknown result type (might be due to invalid IL or missing references)
				//IL_00a5: Unknown result type (might be due to invalid IL or missing references)
				//IL_0070: 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_007d: Unknown result type (might be due to invalid IL or missing references)
				//IL_001d: Unknown result type (might be due to invalid IL or missing references)
				//IL_001f: 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_003e: 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)
				Enhancement val = Enhancements.Of(item);
				if (((Enhancement)(ref val)).IsEmpty)
				{
					Gem[] all = Gems.All;
					foreach (Gem gem in all)
					{
						if (Discovery.IsDiscovered(player, gem))
						{
							Add(gui, player, recipe, new Entry
							{
								Item = item,
								Action = (EnhanceAction)0,
								Gem = gem
							});
						}
					}
				}
				else
				{
					if (Rules.CanDo((EnhanceAction)1, val, settings))
					{
						Add(gui, player, recipe, new Entry
						{
							Item = item,
							Action = (EnhanceAction)1,
							Gem = ((Enhancement)(ref val)).Gem
						});
					}
					Add(gui, player, recipe, new Entry
					{
						Item = item,
						Action = (EnhanceAction)2,
						Gem = ((Enhancement)(ref val)).Gem
					});
				}
			}

			private static void Add(InventoryGui gui, Player player, Recipe recipe, Entry entry)
			{
				//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_000e: Unknown result type (might be due to invalid IL or missing references)
				//IL_0013: Unknown result type (might be due to invalid IL or missing references)
				//IL_0015: Unknown result type (might be due to invalid IL or missing references)
				//IL_00c9: Unknown result type (might be due to invalid IL or missing references)
				Enhancement current = Enhancements.Of(entry.Item);
				bool flag = Enhancer.CanAfford(player, entry.Action, current, entry.Gem);
				AddRecipeToList.Invoke(gui, new object[4] { player, recipe, entry.Item, flag });
				IList obj = AvailableRecipes.Invoke(gui);
				GameObject val = (entry.Element = ElementOf(obj[obj.Count - 1]));
				Entries[val] = entry;
				Ordered.Add(entry);
				Transform val2 = val.transform.Find("name");
				if ((Object)(object)val2 != (Object)null)
				{
					((Component)val2).GetComponent<TMP_Text>().text = Localization.instance.Localize(entry.Item.m_shared.m_name) + ": " + ActionText(entry, current);
				}
			}
		}

		[HarmonyPatch(typeof(InventoryGui), "UpdateRecipe")]
		private static class DetailsPatch
		{
			private static readonly FieldRef<InventoryGui, Color> StationLevelColor = AccessTools.FieldRefAccess<InventoryGui, Color>("m_minStationLevelBasecolor");

			private static void Postfix(InventoryGui __instance, Player player)
			{
				//IL_001f: Unknown result type (might be due to invalid IL or missing references)
				//IL_0024: Unknown result type (might be due to invalid IL or missing references)
				//IL_002b: Unknown result type (might be due to invalid IL or missing references)
				//IL_0030: Unknown result type (might be due to invalid IL or missing references)
				//IL_0032: Unknown result type (might be due to invalid IL or missing references)
				//IL_0037: Unknown result type (might be due to invalid IL or missing references)
				//IL_0039: Unknown result type (might be due to invalid IL or missing references)
				//IL_003e: 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)
				//IL_0046: 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)
				//IL_0052: Unknown result type (might be due to invalid IL or missing references)
				//IL_0055: Unknown result type (might be due to invalid IL or missing references)
				//IL_005a: Unknown result type (might be due to invalid IL or missing references)
				//IL_005c: Unknown result type (might be due to invalid IL or missing references)
				//IL_0061: Unknown result type (might be due to invalid IL or missing references)
				//IL_0066: Unknown result type (might be due to invalid IL or missing references)
				//IL_0093: Unknown result type (might be due to invalid IL or missing references)
				//IL_0094: Unknown result type (might be due to invalid IL or missing references)
				//IL_0096: Unknown result type (might be due to invalid IL or missing references)
				//IL_0098: Unknown result type (might be due to invalid IL or missing references)
				//IL_00c0: 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_0149: 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_0167: Unknown result type (might be due to invalid IL or missing references)
				//IL_016c: Unknown result type (might be due to invalid IL or missing references)
				//IL_018f: Unknown result type (might be due to invalid IL or missing references)
				//IL_0197: Unknown result type (might be due to invalid IL or missing references)
				//IL_019d: Invalid comparison between Unknown and I4
				if (!Active)
				{
					return;
				}
				Entry entry = SelectedEntry(__instance);
				if (entry != null)
				{
					EnhanceSettings current = SettingsSync.Current;
					Enhancement val = Enhancements.Of(entry.Item);
					Slot slot = Enhancements.SlotOf(entry.Item);
					Cost val2 = Rules.CostOf(entry.Action, val, current);
					Gem val3 = Enhancer.GemFor(entry.Action, val, entry.Gem);
					Enhancement next = Rules.Apply(entry.Action, val, entry.Gem);
					__instance.m_recipeName.text = Localization.instance.Localize(entry.Item.m_shared.m_name);
					__instance.m_recipeDecription.text = Describe(val, next, slot, entry.Action, current);
					((Component)__instance.m_itemCraftType).gameObject.SetActive(true);
					__instance.m_itemCraftType.text = ActionText(entry, val);
					((Component)__instance.m_variantButton).gameObject.SetActive(false);
					ShowRequiredStationLevel(__instance, player);
					int num = 0;
					num += ShowRequirement(__instance, player, num, Gems.PrefabName(val3), ((Cost)(ref val2)).Gems);
					num += ShowRequirement(__instance, player, num, "AncientCoin", ((Cost)(ref val2)).Coins);
					for (int i = num; i < __instance.m_recipeRequirementList.Length; i++)
					{
						InventoryGui.HideRequirement(__instance.m_recipeRequirementList[i].transform);
					}
					bool flag = Enhancer.CanAfford(player, entry.Action, val, entry.Gem);
					((Selectable)__instance.m_craftButton).interactable = flag && Rules.CanDo(entry.Action, val, current) && AtListStation(player);
					((Component)__instance.m_craftButton).GetComponentInChildren<TMP_Text>().text = (((int)entry.Action == 0) ? "Set" : (((int)entry.Action == 1) ? "Polish" : "Remove"));
					((Component)__instance.m_craftButton).GetComponent<UITooltip>().m_text = (flag ? "" : Localization.instance.Localize("$msg_missingrequirement"));
				}
			}

			private static void ShowRequiredStationLevel(InventoryGui gui, Player player)
			{
				//IL_0085: Unknown result type (might be due to invalid IL or missing references)
				//IL_008c: Unknown result type (might be due to invalid IL or missing references)
				CraftingStation currentCraftingStation = player.GetCurrentCraftingStation();
				int? num = Eligibility.RequiredLevel(currentCraftingStation);
				((Component)gui.m_minStationLevelIcon).gameObject.SetActive(num.HasValue);
				if (num.HasValue)
				{
					gui.m_minStationLevelText.text = num.Value.ToString();
					bool flag = currentCraftingStation.GetLevel(true) < num.Value;
					((Graphic)gui.m_minStationLevelText).color = ((flag && Mathf.Sin(Time.time * 10f) > 0f) ? Color.red : StationLevelColor.Invoke(gui));
				}
			}

			private static int ShowRequirement(InventoryGui gui, Player player, int index, string prefabName, int amount)
			{
				ItemDrop val = Enhancer.PrefabItem(prefabName);
				if (amount <= 0 || (Object)(object)val == (Object)null || index >= gui.m_recipeRequirementList.Length)
				{
					return 0;
				}
				Requirement val2 = Requirements[index];
				val2.m_resItem = val;
				val2.m_amount = amount;
				val2.m_amountPerLevel = 0;
				return InventoryGui.SetupRequirement(gui.m_recipeRequirementList[index].transform, val2, player, true, 1, 1) ? 1 : 0;
			}

			private static string Describe(Enhancement current, Enhancement next, Slot slot, EnhanceAction action, EnhanceSettings settings)
			{
				//IL_0000: Unknown result type (might be due to invalid IL or missing references)
				//IL_0030: Unknown result type (might be due to invalid IL or missing references)
				//IL_005e: Unknown result type (might be due to invalid IL or missing references)
				//IL_0063: Unknown result type (might be due to invalid IL or missing references)
				//IL_007e: Unknown result type (might be due to invalid IL or missing references)
				//IL_0080: Invalid comparison between Unknown and I4
				//IL_00b6: Unknown result type (might be due to invalid IL or missing references)
				//IL_00e4: Unknown result type (might be due to invalid IL or missing references)
				//IL_00e9: Unknown result type (might be due to invalid IL or missing references)
				//IL_00fd: Unknown result type (might be due to invalid IL or missing references)
				string text = (Rules.IsArmor(slot) ? "armor" : "weapon");
				string text2 = (((Enhancement)(ref current)).IsEmpty ? "No gem set." : ("Now: " + Enhancements.GemName(((Enhancement)(ref current)).Gem) + " " + Enhancement.Roman(((Enhancement)(ref current)).Grade) + ": " + Enhancements.EffectText(((Enhancement)(ref current)).Gem, slot, ((Enhancement)(ref current)).Grade)));
				text2 = (((int)action != 2) ? (text2 + "\n\nAfter: " + Enhancements.GemName(((Enhancement)(ref next)).Gem) + " " + Enhancement.Roman(((Enhancement)(ref next)).Grade) + ": " + Enhancements.EffectText(((Enhancement)(ref next)).Gem, slot, ((Enhancement)(ref next)).Grade)) : (text2 + (settings.RemoveReturnsGem ? "\n\nRemoving gives one gem back." : "\n\n<color=orange>Removing destroys the gem.</color>")));
				text2 = ((!Rules.IsArmor(slot)) ? (text2 + "\n\nWeapon bonuses work while this " + text + " is in your hands.") : (text2 + "\n\nArmor bonuses from all worn pieces add up, to a cap per stat."));
				return text2 + "\nGrades: up to " + Enhancement.Roman(settings.MaxGrade) + ".";
			}
		}

		[HarmonyPatch(typeof(InventoryGui), "OnCraftPressed")]
		private static class CraftButtonPatch
		{
			private static bool Prefix(InventoryGui __instance)
			{
				//IL_003d: Unknown result type (might be due to invalid IL or missing references)
				//IL_0043: Unknown result type (might be due to invalid IL or missing references)
				//IL_006b: Unknown result type (might be due to invalid IL or missing references)
				//IL_0070: 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: Unknown result type (might be due to invalid IL or missing references)
				if (!Active)
				{
					return true;
				}
				Entry entry = SelectedEntry(__instance);
				Player localPlayer = Player.m_localPlayer;
				if (!AtListStation(localPlayer))
				{
					if (localPlayer != null)
					{
						((Character)localPlayer).Message((MessageType)2, "$msg_missingstation", 0, (Sprite)null, false);
					}
					return false;
				}
				if (entry != null && Enhancer.TryPerform(localPlayer, entry.Item, entry.Action, entry.Gem))
				{
					CraftingStation currentCraftingStation = localPlayer.GetCurrentCraftingStation();
					if ((Object)(object)currentCraftingStation != (Object)null)
					{
						currentCraftingStation.m_craftItemEffects.Create(((Component)localPlayer).transform.position, Quaternion.identity, (Transform)null, 1f, -1, default(ZDOID));
					}
					UpdateCraftingPanel.Invoke(__instance, new object[1] { false });
				}
				return false;
			}
		}

		[Serializable]
		[CompilerGenerated]
		private sealed class <>c
		{
			public static readonly <>c <>9 = new <>c();

			public static UnityAction <>9__20_0;

			internal void <EnsureTab>b__20_0()
			{
				InventoryGui instance = InventoryGui.instance;
				if (!((Object)(object)instance == (Object)null))
				{
					SetActive(instance, active: true);
					UpdateCraftingPanel.Invoke(instance, new object[1] { false });
				}
			}
		}

		private static readonly MethodInfo AddRecipeToList = AccessTools.Method(typeof(InventoryGui), "AddRecipeToList", (Type[])null, (Type[])null);

		private static readonly MethodInfo UpdateCraftingPanel = AccessTools.Method(typeof(InventoryGui), "UpdateCraftingPanel", (Type[])null, (Type[])null);

		private static readonly FieldRef<InventoryGui, IList> AvailableRecipes = AccessTools.FieldRefAccess<InventoryGui, IList>("m_availableRecipes");

		private static readonly FieldInfo SelectedRecipe = AccessTools.Field(typeof(InventoryGui), "m_selectedRecipe");

		private static readonly FieldRef<InventoryGui, float> ListBaseSize = AccessTools.FieldRefAccess<InventoryGui, float>("m_recipeListBaseSize");

		private static readonly MethodInfo SetRecipe = AccessTools.Method(typeof(InventoryGui), "SetRecipe", (Type[])null, (Type[])null);

		private static PropertyInfo elementProperty;

		private static readonly Dictionary<GameObject, Entry> Entries = new Dictionary<GameObject, Entry>();

		private static readonly List<Entry> Ordered = new List<Entry>();

		private static readonly Requirement[] Requirements = (Requirement[])(object)new Requirement[2]
		{
			new Requirement(),
			new Requirement()
		};

		private static Button tab;

		private static Entry selected;

		private static CraftingStation listStation;

		public static bool Active { get; private set; }

		private static GameObject ElementOf(object pair)
		{
			if ((object)elementProperty == null)
			{
				elementProperty = AccessTools.Property(pair.GetType(), "InterfaceElement");
			}
			object? value = elementProperty.GetValue(pair);
			return (GameObject)((value is GameObject) ? value : null);
		}

		private static void SetActive(InventoryGui gui, bool active)
		{
			Active = active;
			selected = null;
			if ((Object)(object)tab != (Object)null)
			{
				((Selectable)tab).interactable = !active;
			}
			if (active)
			{
				((Selectable)gui.m_tabCraft).interactable = true;
				((Selectable)gui.m_tabUpgrade).interactable = true;
			}
		}

		private static void EnsureTab(InventoryGui gui)
		{
			//IL_0045: Unknown result type (might be due to invalid IL or missing references)
			//IL_004b: Expected O, but got Unknown
			//IL_0056: Unknown result type (might be due to invalid IL or missing references)
			//IL_005c: Expected O, but got Unknown
			//IL_005d: 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_009d: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ae: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b3: 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_0087: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f8: Unknown result type (might be due to invalid IL or missing references)
			//IL_0102: Expected O, but got Unknown
			//IL_0120: Unknown result type (might be due to invalid IL or missing references)
			//IL_0125: Unknown result type (might be due to invalid IL or missing references)
			//IL_012b: Expected O, but got Unknown
			if ((Object)(object)tab != (Object)null)
			{
				return;
			}
			GameObject val = Object.Instantiate<GameObject>(((Component)gui.m_tabUpgrade).gameObject, ((Component)gui.m_tabUpgrade).transform.parent);
			((Object)val).name = "NorthernGemcraftTab";
			RectTransform val2 = (RectTransform)((Component)gui.m_tabUpgrade).transform;
			RectTransform val3 = (RectTransform)((Component)gui.m_tabCraft).transform;
			float num = val2.anchoredPosition.x - val3.anchoredPosition.x;
			if (Mathf.Abs(num) < 1f)
			{
				Rect rect = val2.rect;
				num = ((Rect)(ref rect)).width + 4f;
			}
			((RectTransform)val.transform).anchoredPosition = val2.anchoredPosition + new Vector2(num, 0f);
			TMP_Text[] componentsInChildren = val.GetComponentsInChildren<TMP_Text>(true);
			for (int i = 0; i < componentsInChildren.Length; i++)
			{
				componentsInChildren[i].text = "ENHANCE";
			}
			tab = val.GetComponent<Button>();
			tab.onClick = new ButtonClickedEvent();
			ButtonClickedEvent onClick = tab.onClick;
			object obj = <>c.<>9__20_0;
			if (obj == null)
			{
				UnityAction val4 = delegate
				{
					InventoryGui instance = InventoryGui.instance;
					if (!((Object)(object)instance == (Object)null))
					{
						SetActive(instance, active: true);
						UpdateCraftingPanel.Invoke(instance, new object[1] { false });
					}
				};
				<>c.<>9__20_0 = val4;
				obj = (object)val4;
			}
			((UnityEvent)onClick).AddListener((UnityAction)obj);
			((Selectable)tab).interactable = true;
			val.transform.SetSiblingIndex(((Component)gui.m_tabUpgrade).transform.GetSiblingIndex() + 1);
		}

		private static Entry SelectedEntry(InventoryGui gui)
		{
			if (selected == null || !((Object)(object)selected.Element != (Object)null) || !Entries.ContainsKey(selected.Element))
			{
				return null;
			}
			return selected;
		}

		private static void Select(InventoryGui gui, Entry entry)
		{
			IList list = AvailableRecipes.Invoke(gui);
			for (int i = 0; i < list.Count; i++)
			{
				if ((Object)(object)ElementOf(list[i]) == (Object)(object)entry.Element)
				{
					SetRecipe.Invoke(gui, new object[2] { i, false });
					SelectedRecipe.SetValue(gui, list[i]);
					selected = entry;
					break;
				}
			}
		}

		private static bool AtListStation(Player player)
		{
			CraftingStation val = (((Object)(object)player != (Object)null) ? player.GetCurrentCraftingStation() : null);
			if ((Object)(object)val != (Object)null)
			{
				return (Object)(object)val == (Object)(object)listStation;
			}
			return false;
		}

		private static string ActionText(Entry entry, Enhancement current)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_0007: Unknown result type (might be due to invalid IL or missing references)
			//IL_0016: 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_000c: Invalid comparison between Unknown and I4
			EnhanceAction action = entry.Action;
			if ((int)action != 0)
			{
				if ((int)action == 1)
				{
					return "Polish to " + Enhancement.Roman(((Enhancement)(ref current)).Grade + 1);
				}
				return "Remove gem";
			}
			return "Set " + Enhancements.GemName(entry.Gem);
		}
	}
}
namespace NorthernGemcraft.Patches
{
	internal static class Percent
	{
		public static float Factor(float percent)
		{
			return Mathf.Max(0f, 1f + percent / 100f);
		}
	}
	[HarmonyPatch(typeof(Player), "GetTotalFoodValue")]
	internal static class MaxHealthPatch
	{
		private static void Postfix(Player __instance, ref float hp)
		{
			if (Enhancements.IsLocalPlayer((Character)(object)__instance))
			{
				hp *= Percent.Factor(Enhancements.Local[(Effect)0]);
			}
		}
	}
	[HarmonyPatch(typeof(SEMan), "ModifyStaminaRegen")]
	internal static class StaminaRegenPatch
	{
		private static readonly FieldRef<SEMan, Character> Owner = AccessTools.FieldRefAccess<SEMan, Character>("m_character");

		private static void Postfix(SEMan __instance, ref float staminaMultiplier)
		{
			if (Enhancements.IsLocalPlayer(Owner.Invoke(__instance)))
			{
				staminaMultiplier *= Percent.Factor(Enhancements.Local[(Effect)2]);
			}
		}
	}
	[HarmonyPatch(typeof(SEMan), "ModifyEitrRegen")]
	internal static class EitrRegenPatch
	{
		private static readonly FieldRef<SEMan, Character> Owner = AccessTools.FieldRefAccess<SEMan, Character>("m_character");

		private static void Postfix(SEMan __instance, ref float eitrMultiplier)
		{
			if (Enhancements.IsLocalPlayer(Owner.Invoke(__instance)))
			{
				eitrMultiplier *= Percent.Factor(Enhancements.Local[(Effect)3]);
			}
		}
	}
	[HarmonyPatch(typeof(SEMan), "ModifyHealthRegen")]
	internal static class HealthRegenPatch
	{
		private static readonly FieldRef<SEMan, Character> Owner = AccessTools.FieldRefAccess<SEMan, Character>("m_character");

		private static void Postfix(SEMan __instance, ref float regenMultiplier)
		{
			if (Enhancements.IsLocalPlayer(Owner.Invoke(__instance)))
			{
				regenMultiplier *= Percent.Factor(Enhancements.Local[(Effect)1]);
			}
		}
	}
	[HarmonyPatch(typeof(SEMan), "ModifyRunStaminaDrain")]
	internal static class RunStaminaPatch
	{
		private static readonly FieldRef<SEMan, Character> Owner = AccessTools.FieldRefAccess<SEMan, Character>("m_character");

		private static void Postfix(SEMan __instance, ref float drain)
		{
			if (drain > 0f && Enhancements.IsLocalPlayer(Owner.Invoke(__instance)))
			{
				drain *= Percent.Factor(Enhancements.Local[(Effect)7]);
			}
		}
	}
	[HarmonyPatch(typeof(Player), "GetEquipmentMovementModifier")]
	internal static class MoveSpeedPatch
	{
		private static void Postfix(Player __instance, ref float __result)
		{
			if (Enhancements.IsLocalPlayer((Character)(object)__instance))
			{
				__result += Enhancements.Local[(Effect)5] / 100f;
			}
		}
	}
	[HarmonyPatch(typeof(Player), "GetMaxCarryWeight")]
	internal static class CarryWeightPatch
	{
		private static void Postfix(Player __instance, ref float __result)
		{
			if (Enhancements.IsLocalPlayer((Character)(object)__instance))
			{
				__result = Mathf.Max(0f, __result + Enhancements.Local[(Effect)6]);
			}
		}
	}
	[HarmonyPatch(typeof(Character), "RPC_Damage")]
	internal static class DamageTakenPatch
	{
		private static void Prefix(Character __instance, HitData hit)
		{
			if (hit != null && Enhancements.IsLocalPlayer(__instance))
			{
				float num = Enhancements.Local[(Effect)4];
				if (num != 0f)
				{
					float num2 = Percent.Factor(num);
					hit.m_damage.m_blunt *= num2;
					hit.m_damage.m_slash *= num2;
					hit.m_damage.m_pierce *= num2;
				}
			}
		}
	}
	[HarmonyPatch(typeof(Character), "Damage")]
	internal static class LifeStealPatch
	{
		private static void Prefix(Character __instance, HitData hit)
		{
			Player localPlayer = Player.m_localPlayer;
			if (hit != null && !((Object)(object)localPlayer == (Object)null) && !((Object)(object)__instance == (Object)(object)localPlayer) && !((Object)(object)hit.GetAttacker() != (Object)(object)localPlayer) && !((Character)localPlayer).IsDead() && !__instance.IsPlayer() && !__instance.IsTamed() && !__instance.IsDead())
			{
				float num = Enhancements.Local[(Effect)11];
				float num2 = Mathf.Min(hit.GetTotalDamage(), __instance.GetHealth()) * num / 100f;
				if (num2 > 0.05f)
				{
					((Character)localPlayer).Heal(num2, true);
				}
			}
		}
	}
	[HarmonyPatch(typeof(Attack), "GetAttackStamina")]
	internal static class AttackStaminaPatch
	{
		private static readonly FieldRef<Attack, Humanoid> Attacker = AccessTools.FieldRefAccess<Attack, Humanoid>("m_character");

		private static void Postfix(Attack __instance, ref float __result)
		{
			if (__result > 0f && Enhancements.IsLocalPlayer((Character)(object)Attacker.Invoke(__instance)))
			{
				__result *= Percent.Factor(Enhancements.Local[(Effect)9]);
			}
		}
	}
	[HarmonyPatch(typeof(Attack), "GetAttackEitr", new Type[]
	{
		typeof(Character),
		typeof(ItemData)
	})]
	internal static class AttackEitrPatch
	{
		private static void Postfix(Character character, ref float __result)
		{
			if (__result > 0f && Enhancements.IsLocalPlayer(character))
			{
				__result *= Percent.Factor(Enhancements.Local[(Effect)10]);
			}
		}
	}
	[HarmonyPatch(typeof(ItemData), "GetMaxDurability", new Type[] { typeof(int) })]
	internal static class DurabilityPatch
	{
		private static void Postfix(ItemData __instance, ref float __result)
		{
			float num = Enhancements.OwnEffect(__instance, (Effect)8);
			if (num != 0f)
			{
				__result *= Percent.Factor(num);
			}
		}
	}
	[HarmonyPatch(typeof(ItemData), "GetDamage", new Type[]
	{
		typeof(int),
		typeof(float)
	})]
	internal static class WeaponDamagePatch
	{
		private static void Postfix(ItemData __instance, ref DamageTypes __result)
		{
			float num = Enhancements.OwnEffect(__instance, (Effect)12);
			if (num != 0f)
			{
				((DamageTypes)(ref __result)).Modify(Percent.Factor(num));
			}
		}
	}
	[HarmonyPatch(typeof(ItemData), "GetTooltip", new Type[]
	{
		typeof(ItemData),
		typeof(int),
		typeof(bool),
		typeof(float),
		typeof(int),
		typeof(bool)
	})]
	internal static class TooltipPatch
	{
		private const string GemColor = "#E8B04B";

		private static void Postfix(ItemData item, ref string __result)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_0008: Unknown result type (might be due to invalid IL or missing references)
			//IL_000d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0017: Unknown result type (might be due to invalid IL or missing references)
			//IL_0021: Unknown result type (might be due to invalid IL or missing references)
			//IL_0023: Invalid comparison between Unknown and I4
			//IL_008b: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bc: Unknown result type (might be due to invalid IL or missing references)
			//IL_0026: Unknown result type (might be due to invalid IL or missing references)
			//IL_00cc: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d1: Unknown result type (might be due to invalid IL or missing references)
			//IL_004a: Unknown result type (might be due to invalid IL or missing references)
			//IL_004b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0069: Unknown result type (might be due to invalid IL or missing references)
			//IL_006a: Unknown result type (might be due to invalid IL or missing references)
			Enhancement val = Enhancements.Of(item);
			Slot val2 = Enhancements.SlotOf(item);
			if (!((Enhancement)(ref val)).IsEmpty && (int)val2 != 0)
			{
				EnhanceSettings current = SettingsSync.Current;
				if ((int)val2 == 6 && current.CanEnhance(val2))
				{
					Attack attack = item.m_shared.m_attack;
					__result = WithEnhanced(__result, "$item_staminause", attack.m_attackStamina, Rules.ItemOwnEffect(val, val2, (Effect)9, current));
					__result = WithEnhanced(__result, "$item_eitruse", attack.m_attackEitr, Rules.ItemOwnEffect(val, val2, (Effect)10, current));
				}
				string text = "\n\n<color=#E8B04B>" + Enhancements.GemName(((Enhancement)(ref val)).Gem) + " " + Enhancement.Roman(((Enhancement)(ref val)).Grade) + "</color>";
				if (current.CanEnhance(val2))
				{
					text = text + ": " + Enhancements.EffectText(((Enhancement)(ref val)).Gem, val2, ((Enhancement)(ref val)).Grade);
					text += Totals(current);
				}
				else
				{
					text += " (inactive)";
				}
				__result += text;
			}
		}

		private static string Totals(EnhanceSettings settings)
		{
			//IL_0025: 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_0032: 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_0064: Unknown result type (might be due to invalid IL or missing references)
			//IL_007c: Unknown result type (might be due to invalid IL or missing references)
			EffectTotals local = Enhancements.Local;
			StringBuilder stringBuilder = new StringBuilder("\n\n<color=#E8B04B>All your gems together</color>");
			int num = 0;
			Effect[] all = Effects.All;
			foreach (Effect val in all)
			{
				float num2 = local[val];
				float? num3 = settings.CapOf(val);
				if (!Effects.OnItem(val) && (num2 != 0f || num3.HasValue))
				{
					stringBuilder.Append("\n  ").Append(Effects.Format(val, num2));
					if (num3.HasValue)
					{
						bool flag = Effects.Goodness(val, num2) >= num3.Value - 0.01f;
						stringBuilder.Append(flag ? $" <color=yellow>(cap {num3.Value:0.#}% reached)</color>" : $" (cap {num3.Value:0.#}%)");
					}
					num++;
				}
			}
			if (num <= 0)
			{
				return "";
			}
			return stringBuilder.ToString();
		}

		private static string WithEnhanced(string tooltip, string token, float baseValue, float percent)
		{
			if (baseValue <= 0f || percent == 0f)
			{
				return tooltip;
			}
			string text = $"\n{token}: <color=orange>{baseValue}</color>";
			float num = baseValue * Percent.Factor(percent);
			return tooltip.Replace(text, string.Format("{0} (<color={1}>{2:0.#}</color>)", text, "#E8B04B", num));
		}
	}
	[HarmonyPatch(typeof(InventoryGui), "DoCrafting")]
	internal static class UpgradeKeepsGemPatch
	{
		private struct Carried
		{
			public Enhancement Enhancement;

			public Vector2i Position;

			public string Name;
		}

		private static readonly FieldRef<InventoryGui, ItemData> UpgradeItem = AccessTools.FieldRefAccess<InventoryGui, ItemData>("m_craftUpgradeItem");

		private static void Prefix(InventoryGui __instance, out Carried? __state)
		{
			//IL_000e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0013: 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_0029: 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)
			ItemData val = UpgradeItem.Invoke(__instance);
			Enhancement enhancement = Enhancements.Of(val);
			__state = (((Enhancement)(ref enhancement)).IsEmpty ? ((Carried?)null) : new Carried?(new Carried
			{
				Enhancement = enhancement,
				Position = val.m_gridPos,
				Name = val.m_shared.m_name
			}));
		}

		private static void Postfix(Player player, Carried? __state)
		{
			//IL_0022: 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_0059: Unknown result type (might be due to invalid IL or missing references)
			//IL_005e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0081: 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)
			if (!__state.HasValue || (Object)(object)player == (Object)null)
			{
				return;
			}
			Carried value = __state.Value;
			ItemData itemAt = ((Humanoid)player).GetInventory().GetItemAt(value.Position.x, value.Position.y);
			if (itemAt == null || !(itemAt.m_shared.m_name == value.Name))
			{
				return;
			}
			Enhancement val = Enhancements.Of(itemAt);
			if (((Enhancement)(ref val)).IsEmpty)
			{
				bool num = itemAt.m_durability >= itemAt.GetMaxDurability() - 0.01f;
				Enhancements.Write(itemAt, value.Enhancement);
				if (num)
				{
					itemAt.m_durability = itemAt.GetMaxDurability();
				}
				Plugin.Detail($"Kept {value.Enhancement} on upgraded {value.Name}");
			}
		}
	}
	[HarmonyPatch(typeof(Player), "GetEquipmentModifierPlusSE")]
	internal static class MovementTotalPatch
	{
		private static void Postfix(Player __instance, int index, ref float __result)
		{
			if (index == 0 && Enhancements.IsLocalPlayer((Character)(object)__instance))
			{
				__result += Enhancements.Local[(Effect)5] / 100f;
			}
		}
	}
}