Decompiled source of AeresProfessions v0.1.0

AeresProfessions.dll

Decompiled 3 weeks ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Permissions;
using BepInEx;
using BepInEx.Configuration;
using HarmonyLib;
using Jotunn.Entities;
using Jotunn.Managers;
using Jotunn.Utils;
using Microsoft.CodeAnalysis;
using UnityEngine;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: IgnoresAccessChecksTo("assembly_valheim")]
[assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")]
[assembly: AssemblyCompany("AeresProfessions")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("0.1.0.0")]
[assembly: AssemblyInformationalVersion("0.1.0")]
[assembly: AssemblyProduct("AeresProfessions")]
[assembly: AssemblyTitle("AeresProfessions")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("0.1.0.0")]
[module: UnverifiableCode]
[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 ValheimProfessions
{
	internal static class Crafting
	{
		private enum Mode
		{
			None,
			Crafting,
			Building
		}

		[HarmonyPatch(typeof(InventoryGui), "DoCrafting")]
		private static class DoCraftingPatch
		{
			private static void Prefix(InventoryGui __instance, Player player)
			{
				_mode = Mode.Crafting;
				CraftingStation val = (((Object)(object)player != (Object)null) ? player.GetCurrentCraftingStation() : null);
				_station = (((Object)(object)val != (Object)null) ? val.m_name.ToLowerInvariant() : "");
				Recipe craftRecipe = __instance.m_craftRecipe;
				_craftsFood = (Object)(object)craftRecipe != (Object)null && (Object)(object)craftRecipe.m_item != (Object)null && IsFood(craftRecipe.m_item.m_itemData.m_shared);
			}

			private static Exception Finalizer(Exception __exception)
			{
				_mode = Mode.None;
				return __exception;
			}
		}

		[HarmonyPatch(typeof(Player), "UpdatePlacement")]
		private static class UpdatePlacementPatch
		{
			private static void Prefix()
			{
				if (_mode == Mode.None)
				{
					_mode = Mode.Building;
				}
			}

			private static Exception Finalizer(Exception __exception)
			{
				if (_mode == Mode.Building)
				{
					_mode = Mode.None;
				}
				return __exception;
			}
		}

		[HarmonyPatch(typeof(Player), "ConsumeResources")]
		private static class ConsumeResourcesPatch
		{
			private static void Postfix(Player __instance, Requirement[] requirements, int qualityLevel, int multiplier)
			{
				if (_mode == Mode.None || (Object)(object)__instance != (Object)(object)Player.m_localPlayer || __instance.NoCostCheat())
				{
					return;
				}
				ProfessionType? professionType = Resolve(requirements);
				if (!professionType.HasValue)
				{
					return;
				}
				int num = 0;
				Requirement[] array = requirements;
				foreach (Requirement val in array)
				{
					if ((Object)(object)val.m_resItem != (Object)null)
					{
						num += val.GetAmount(qualityLevel) * multiplier;
					}
				}
				ProfessionManager.AddXp(__instance, professionType.Value, (float)num * 1.5f);
				if (!ProfessionInfo.SavesMaterials(professionType.Value) || Random.value >= ProfessionManager.UtilityBonus(__instance, professionType.Value))
				{
					return;
				}
				Inventory inventory = ((Humanoid)__instance).GetInventory();
				array = requirements;
				foreach (Requirement val2 in array)
				{
					if (!((Object)(object)val2.m_resItem == (Object)null))
					{
						int num2 = val2.GetAmount(qualityLevel) * multiplier;
						if (num2 > 0)
						{
							inventory.AddItem(((Component)val2.m_resItem).gameObject, num2);
						}
					}
				}
				((Character)__instance).Message((MessageType)1, "Materials saved!", 0, (Sprite)null, false);
			}
		}

		private static Mode _mode;

		private static string _station = "";

		private static bool _craftsFood;

		private static readonly string[] StoneItems = new string[3] { "Stone", "BlackMarble", "Grausten" };

		private static bool IsFood(SharedData shared)
		{
			if (shared != null)
			{
				if (!(shared.m_food > 0f) && !(shared.m_foodStamina > 0f))
				{
					return shared.m_foodEitr > 0f;
				}
				return true;
			}
			return false;
		}

		private static ProfessionType? Resolve(Requirement[] requirements)
		{
			if (_mode == Mode.Crafting)
			{
				if (_craftsFood)
				{
					return ProfessionType.TavernKeeper;
				}
				if (_station.Contains("forge"))
				{
					return ProfessionType.Blacksmith;
				}
				if (_station.Contains("cauldron") || _station.Contains("mead"))
				{
					return ProfessionType.Alchemist;
				}
				if (_station.Contains("stonecutter"))
				{
					return ProfessionType.Mason;
				}
			}
			else if (_mode == Mode.Building)
			{
				foreach (Requirement val in requirements)
				{
					if (!((Object)(object)val.m_resItem == (Object)null) && Array.IndexOf(StoneItems, ((Object)val.m_resItem).name) >= 0)
					{
						return ProfessionType.Mason;
					}
				}
			}
			return null;
		}
	}
	internal static class Patches
	{
		[HarmonyPatch(typeof(MineRock5), "Damage")]
		private static class MineRock5Patch
		{
			private static void Prefix(HitData hit)
			{
				Player val = LocalAttacker(hit);
				if (!((Object)(object)val == (Object)null) && !(hit.m_damage.m_pickaxe <= 0f))
				{
					hit.m_damage.m_pickaxe *= ProfessionManager.PerkMultiplier(val, ProfessionType.Miner);
					Apply(hit, ProfessionType.Miner, hit.m_damage.m_pickaxe);
				}
			}
		}

		[HarmonyPatch(typeof(MineRock), "Damage")]
		private static class MineRockPatch
		{
			private static void Prefix(HitData hit)
			{
				Player val = LocalAttacker(hit);
				if (!((Object)(object)val == (Object)null) && !(hit.m_damage.m_pickaxe <= 0f))
				{
					hit.m_damage.m_pickaxe *= ProfessionManager.PerkMultiplier(val, ProfessionType.Miner);
					Apply(hit, ProfessionType.Miner, hit.m_damage.m_pickaxe);
				}
			}
		}

		[HarmonyPatch(typeof(TreeBase), "Damage")]
		private static class TreeBasePatch
		{
			private static void Prefix(HitData hit)
			{
				Player val = LocalAttacker(hit);
				if (!((Object)(object)val == (Object)null) && !(hit.m_damage.m_chop <= 0f))
				{
					hit.m_damage.m_chop *= ProfessionManager.PerkMultiplier(val, ProfessionType.Woodcutter);
					Apply(hit, ProfessionType.Woodcutter, hit.m_damage.m_chop);
				}
			}
		}

		[HarmonyPatch(typeof(TreeLog), "Damage")]
		private static class TreeLogPatch
		{
			private static void Prefix(HitData hit)
			{
				Player val = LocalAttacker(hit);
				if (!((Object)(object)val == (Object)null) && !(hit.m_damage.m_chop <= 0f))
				{
					hit.m_damage.m_chop *= ProfessionManager.PerkMultiplier(val, ProfessionType.Woodcutter);
					Apply(hit, ProfessionType.Woodcutter, hit.m_damage.m_chop);
				}
			}
		}

		[HarmonyPatch(typeof(Character), "Damage")]
		private static class CharacterDamagePatch
		{
			private static void Prefix(Character __instance, HitData hit)
			{
				if (__instance is Player)
				{
					return;
				}
				Player val = LocalAttacker(hit);
				if (!((Object)(object)val == (Object)null))
				{
					float num = ProfessionManager.PerkMultiplier(val, ProfessionType.Hunter);
					if (num != 1f)
					{
						hit.ApplyModifier(num);
					}
					Apply(hit, ProfessionType.Hunter, hit.GetTotalDamage());
				}
			}
		}

		[HarmonyPatch(typeof(Pickable), "Interact")]
		private static class PickableInteractPatch
		{
			private static void Prefix(Pickable __instance, Humanoid character, out bool __state)
			{
				__state = (Object)(object)character == (Object)(object)Player.m_localPlayer && !__instance.m_picked && __instance.m_enabled != 0 && (Object)(object)__instance.m_itemPrefab != (Object)null && Harvestables.Contains(((Object)__instance.m_itemPrefab).name);
				if (__state && IsRepeat(((Object)__instance).GetInstanceID()))
				{
					__state = false;
				}
			}

			private static void Postfix(Pickable __instance, bool __state)
			{
				if (__state)
				{
					Player localPlayer = Player.m_localPlayer;
					ProfessionManager.AddXp(localPlayer, ProfessionType.Farmer, 4f);
					if (Random.value < ProfessionManager.UtilityBonus(localPlayer, ProfessionType.Farmer))
					{
						((Humanoid)localPlayer).GetInventory().AddItem(__instance.m_itemPrefab, __instance.m_amount);
						((Character)localPlayer).Message((MessageType)1, "Double harvest!", 0, (Sprite)null, false);
					}
				}
			}
		}

		[HarmonyPatch(typeof(Player), "PlacePiece")]
		private static class PlantingPatch
		{
			private static void Postfix(Player __instance, Piece piece, bool cheated)
			{
				if (!cheated && !((Object)(object)__instance != (Object)(object)Player.m_localPlayer) && (Object)(object)piece != (Object)null && (Object)(object)((Component)piece).GetComponent<Plant>() != (Object)null)
				{
					ProfessionManager.AddXp(__instance, ProfessionType.Farmer, 3f);
				}
			}
		}

		[HarmonyPatch(typeof(CookingStation), "OnInteract")]
		private static class CookingCollectPatch
		{
			private static void Prefix(CookingStation __instance, Humanoid user, out bool __state)
			{
				__state = (Object)(object)user == (Object)(object)Player.m_localPlayer && __instance.HaveDoneItem() && !IsRepeat(((Object)__instance).GetInstanceID());
			}

			private static void Postfix(bool __state)
			{
				if (__state)
				{
					ProfessionManager.AddXp(Player.m_localPlayer, ProfessionType.TavernKeeper, 8f);
				}
			}
		}

		[HarmonyPatch(typeof(Player), "EatFood")]
		private static class EatFoodPatch
		{
			private static void Postfix(Player __instance, ItemData item, bool __result)
			{
				if (!__result || (Object)(object)__instance != (Object)(object)Player.m_localPlayer)
				{
					return;
				}
				float num = ProfessionManager.UtilityBonus(__instance, ProfessionType.TavernKeeper);
				if (num <= 0f)
				{
					return;
				}
				foreach (Food food in __instance.GetFoods())
				{
					if (food.m_item.m_shared.m_name == item.m_shared.m_name)
					{
						food.m_time = item.m_shared.m_foodBurnTime * (1f + num);
					}
				}
			}
		}

		[HarmonyPatch(typeof(Ship), "CustomFixedUpdate")]
		private static class ShipXpPatch
		{
			private static void Postfix(Ship __instance, float fixedDeltaTime)
			{
				if (!IsControlledByLocalPlayer(__instance))
				{
					return;
				}
				_sailTimer += fixedDeltaTime;
				if (!(_sailTimer < 1f))
				{
					_sailTimer -= 1f;
					float num = Mathf.Abs(__instance.GetSpeed());
					if (!(num < 1f))
					{
						ProfessionManager.AddXp(Player.m_localPlayer, ProfessionType.Mariner, num * 0.5f);
					}
				}
			}
		}

		[HarmonyPatch(typeof(FishingFloat), "Catch")]
		private static class FishingCatchPatch
		{
			private static void Postfix(Fish fish, Character owner)
			{
				Player localPlayer = Player.m_localPlayer;
				if (!((Object)(object)localPlayer == (Object)null) && !((Object)(object)owner != (Object)(object)localPlayer))
				{
					ItemDrop val = (((Object)(object)fish != (Object)null) ? ((Component)fish).GetComponent<ItemDrop>() : null);
					int num = ((!((Object)(object)val != (Object)null)) ? 1 : val.m_itemData.m_quality);
					ProfessionManager.AddXp(localPlayer, ProfessionType.Mariner, 12f + 4f * (float)num);
				}
			}
		}

		[HarmonyPatch(typeof(Ship), "GetSailForce")]
		private static class SailForcePatch
		{
			private static void Postfix(Ship __instance, ref Vector3 __result)
			{
				//IL_001f: 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)
				if (IsControlledByLocalPlayer(__instance))
				{
					float num = ProfessionManager.UtilityBonus(Player.m_localPlayer, ProfessionType.Mariner);
					if (num > 0f)
					{
						__result *= 1f + num;
					}
				}
			}
		}

		private static readonly HashSet<string> Harvestables = new HashSet<string>
		{
			"Carrot", "CarrotSeeds", "Turnip", "TurnipSeeds", "Onion", "OnionSeeds", "Barley", "Flax", "Raspberry", "Blueberries",
			"Cloudberry", "Mushroom", "MushroomYellow", "MushroomBlue", "MushroomJotunPuffs", "MushroomMagecap", "Dandelion", "Thistle"
		};

		private static int _lastTouchedId;

		private static float _lastTouchedAt;

		private static float _sailTimer;

		private static Player LocalAttacker(HitData hit)
		{
			Character obj = ((hit != null) ? hit.GetAttacker() : null);
			Player val = (Player)(object)((obj is Player) ? obj : null);
			if (!((Object)(object)val != (Object)null) || !((Object)(object)val == (Object)(object)Player.m_localPlayer))
			{
				return null;
			}
			return val;
		}

		private static void Apply(HitData hit, ProfessionType type, float baseDamage)
		{
			Player val = LocalAttacker(hit);
			if (!((Object)(object)val == (Object)null))
			{
				ProfessionManager.AddXp(val, type, baseDamage * 0.1f);
			}
		}

		private static bool IsRepeat(int instanceId)
		{
			if (instanceId == _lastTouchedId && Time.time - _lastTouchedAt < 0.75f)
			{
				return true;
			}
			_lastTouchedId = instanceId;
			_lastTouchedAt = Time.time;
			return false;
		}

		private static bool IsControlledByLocalPlayer(Ship ship)
		{
			Player localPlayer = Player.m_localPlayer;
			if ((Object)(object)localPlayer != (Object)null)
			{
				return (Object)(object)localPlayer.GetControlledShip() == (Object)(object)ship;
			}
			return false;
		}
	}
	[BepInPlugin("com.danle.professions", "Professions", "0.1.0")]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	[NetworkCompatibility(/*Could not decode attribute arguments.*/)]
	public class Plugin : BaseUnityPlugin
	{
		public const string PluginGuid = "com.danle.professions";

		public const string PluginName = "Professions";

		public const string PluginVersion = "0.1.0";

		public static ConfigEntry<int> MaxLevel;

		public static ConfigEntry<float> BaseXp;

		public static ConfigEntry<float> XpMultiplier;

		public static ConfigEntry<float> PerkPercentPerLevel;

		public static ConfigEntry<float> UtilityPercentPerLevel;

		public static ConfigEntry<bool> AllowSwitching;

		public static ConfigEntry<KeyboardShortcut> MenuKey;

		public static ConfigEntry<bool> ShowHint;

		private void Awake()
		{
			//IL_0020: 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)
			MenuKey = ((BaseUnityPlugin)this).Config.Bind<KeyboardShortcut>("Menu", "OpenKey", new KeyboardShortcut((KeyCode)107, (KeyCode[])(object)new KeyCode[1] { (KeyCode)308 }), "Opens and closes the professions menu. Supports modifiers, e.g. LeftAlt + K. Change it if it conflicts with another mod.");
			ShowHint = ((BaseUnityPlugin)this).Config.Bind<bool>("Menu", "ShowHint", true, "Show a one-time reminder of the menu key after spawning while no profession is chosen.");
			MaxLevel = SyncedConfig("Progression", "MaxLevel", 50, "Highest reachable profession level.");
			BaseXp = SyncedConfig("Progression", "BaseXp", 60f, "Scales the XP curve. Higher = slower leveling.");
			XpMultiplier = SyncedConfig("Progression", "XpMultiplier", 1f, "Multiplier applied to all XP gained.");
			PerkPercentPerLevel = SyncedConfig("Perks", "PercentPerLevel", 1f, "Damage bonus (percent) gained per level above 1 (Miner, Woodcutter, Hunter).");
			UtilityPercentPerLevel = SyncedConfig("Perks", "UtilityPercentPerLevel", 0.5f, "Chance or speed bonus (percent) gained per level above 1 (Farmer, Blacksmith, Alchemist, Mason, Mariner).");
			AllowSwitching = SyncedConfig("Rules", "AllowSwitching", value: true, "Whether players may change profession after choosing one.");
			CommandManager.Instance.AddConsoleCommand((ConsoleCommand)(object)new ProfessionCommand());
			((Component)this).gameObject.AddComponent<ProfessionMenu>();
			new Harmony("com.danle.professions").PatchAll();
		}

		private ConfigEntry<T> SyncedConfig<T>(string section, string key, T value, string description)
		{
			//IL_0014: 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_0021: Expected O, but got Unknown
			//IL_0021: Unknown result type (might be due to invalid IL or missing references)
			//IL_002b: Expected O, but got Unknown
			return ((BaseUnityPlugin)this).Config.Bind<T>(section, key, value, new ConfigDescription(description, (AcceptableValueBase)null, new object[1] { (object)new ConfigurationManagerAttributes
			{
				IsAdminOnly = true
			} }));
		}
	}
	public class ProfessionCommand : ConsoleCommand
	{
		public override string Name => "profession";

		public override string Help => "profession [choose <" + string.Join("|", Enum.GetNames(typeof(ProfessionType))).ToLowerInvariant() + ">] - show or choose your profession";

		public override void Run(string[] args)
		{
			Player localPlayer = Player.m_localPlayer;
			if ((Object)(object)localPlayer == (Object)null)
			{
				Console.instance.Print("Load into a world first.");
				return;
			}
			if (args.Length >= 2 && args[0].ToLowerInvariant() == "choose")
			{
				if (!Enum.TryParse<ProfessionType>(args[1], ignoreCase: true, out var result))
				{
					Console.instance.Print("Unknown profession '" + args[1] + "'.");
					return;
				}
				if (!ProfessionManager.TryChoose(localPlayer, result, out var error))
				{
					Console.instance.Print(error);
					return;
				}
			}
			Console.instance.Print(ProfessionManager.Describe(localPlayer));
		}

		public override List<string> CommandOptionList()
		{
			return new List<string> { "choose" };
		}
	}
	public static class ProfessionManager
	{
		private const string ActiveKey = "Professions.Active";

		private const string XpKeyPrefix = "Professions.XP.";

		public static ProfessionType? GetActive(Player player)
		{
			if ((Object)(object)player == (Object)null)
			{
				return null;
			}
			if (player.m_customData.TryGetValue("Professions.Active", out var value) && Enum.TryParse<ProfessionType>(value, out var result))
			{
				return result;
			}
			return null;
		}

		public static bool TryChoose(Player player, ProfessionType type, out string error)
		{
			error = null;
			ProfessionType? active = GetActive(player);
			if (active == type)
			{
				error = $"You are already a {type}.";
				return false;
			}
			if (active.HasValue && !Plugin.AllowSwitching.Value)
			{
				error = $"You are already a {active}; switching is disabled on this server.";
				return false;
			}
			player.m_customData["Professions.Active"] = type.ToString();
			return true;
		}

		public static long GetXp(Player player, ProfessionType type)
		{
			if (player.m_customData.TryGetValue("Professions.XP." + type, out var value) && long.TryParse(value, out var result))
			{
				return result;
			}
			return 0L;
		}

		public static long XpForLevel(int level)
		{
			if (level <= 1)
			{
				return 0L;
			}
			return (long)((double)Plugin.BaseXp.Value * Math.Pow(level - 1, 1.6));
		}

		public static int GetLevel(Player player, ProfessionType type)
		{
			long xp = GetXp(player, type);
			int i;
			for (i = 1; i < Plugin.MaxLevel.Value && xp >= XpForLevel(i + 1); i++)
			{
			}
			return i;
		}

		public static void AddXp(Player player, ProfessionType type, float amount)
		{
			if (!((Object)(object)player == (Object)null) && !(amount <= 0f) && GetActive(player) == type)
			{
				int level = GetLevel(player, type);
				long num = Math.Max(1L, (long)Math.Round(amount * Plugin.XpMultiplier.Value));
				player.m_customData["Professions.XP." + type] = (GetXp(player, type) + num).ToString();
				int level2 = GetLevel(player, type);
				if (level2 > level)
				{
					((Character)player).Message((MessageType)2, $"{ProfessionInfo.DisplayName(type)} level {level2}!", 0, (Sprite)null, false);
				}
			}
		}

		public static float PerkMultiplier(Player player, ProfessionType type)
		{
			if (GetActive(player) != type)
			{
				return 1f;
			}
			int level = GetLevel(player, type);
			return 1f + (float)(level - 1) * Plugin.PerkPercentPerLevel.Value / 100f;
		}

		public static float UtilityBonus(Player player, ProfessionType type)
		{
			if (GetActive(player) != type)
			{
				return 0f;
			}
			return (float)(GetLevel(player, type) - 1) * Plugin.UtilityPercentPerLevel.Value / 100f;
		}

		public static float BonusPercent(Player player, ProfessionType type)
		{
			float num = (ProfessionInfo.IsDamagePerk(type) ? Plugin.PerkPercentPerLevel.Value : Plugin.UtilityPercentPerLevel.Value);
			return (float)(GetLevel(player, type) - 1) * num;
		}

		public static bool CanChoose(Player player, ProfessionType type)
		{
			ProfessionType? active = GetActive(player);
			if (active != type)
			{
				if (active.HasValue)
				{
					return Plugin.AllowSwitching.Value;
				}
				return true;
			}
			return false;
		}

		public static string Describe(Player player)
		{
			ProfessionType? active = GetActive(player);
			if (!active.HasValue)
			{
				string text = string.Join("|", Enum.GetNames(typeof(ProfessionType))).ToLowerInvariant();
				return "No profession chosen. Use: profession choose <" + text + ">";
			}
			ProfessionType value = active.Value;
			int level = GetLevel(player, value);
			long xp = GetXp(player, value);
			string text2 = ((level >= Plugin.MaxLevel.Value) ? "max level" : $"{XpForLevel(level + 1) - xp} XP to next");
			string text3 = BonusPercent(player, value).ToString("0.#");
			return $"{ProfessionInfo.DisplayName(value)} level {level} ({xp} XP, {text2}): +{text3}% {ProfessionInfo.Perk(value)}";
		}
	}
	public class ProfessionMenu : MonoBehaviour
	{
		private static class Styles
		{
			public static GUIStyle Panel;

			public static GUIStyle Title;

			public static GUIStyle Subtitle;

			public static GUIStyle Warning;

			public static GUIStyle Message;

			public static GUIStyle Row;

			public static GUIStyle RowActive;

			public static GUIStyle Name;

			public static GUIStyle Level;

			public static GUIStyle ActiveTag;

			public static GUIStyle Small;

			public static GUIStyle Perk;

			public static GUIStyle Button;

			public static Texture2D BarBack;

			public static Texture2D BarFill;

			private static Texture2D _panelTex;

			private static readonly Color Gold = new Color(0.96f, 0.76f, 0.3f);

			private static readonly Color Text = new Color(0.93f, 0.9f, 0.84f);

			private static readonly Color Muted = new Color(0.68f, 0.64f, 0.58f);

			private static Texture2D Tex(Color color)
			{
				//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_000a: Unknown result type (might be due to invalid IL or missing references)
				//IL_0010: 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_001f: Expected O, but got Unknown
				Texture2D val = new Texture2D(1, 1);
				val.SetPixel(0, 0, color);
				val.Apply();
				((Object)val).hideFlags = (HideFlags)61;
				return val;
			}

			public static void Ensure()
			{
				//IL_0029: 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_0056: Unknown result type (might be due to invalid IL or missing references)
				//IL_0079: Unknown result type (might be due to invalid IL or missing references)
				//IL_0098: 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_00cc: 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_00f6: Unknown result type (might be due to invalid IL or missing references)
				//IL_00fd: Expected O, but got Unknown
				//IL_0107: Unknown result type (might be due to invalid IL or missing references)
				//IL_010c: Unknown result type (might be due to invalid IL or missing references)
				//IL_0114: Unknown result type (might be due to invalid IL or missing references)
				//IL_011d: Unknown result type (might be due to invalid IL or missing references)
				//IL_0127: Expected O, but got Unknown
				//IL_012c: Expected O, but got Unknown
				//IL_01c1: Unknown result type (might be due to invalid IL or missing references)
				//IL_01d3: Unknown result type (might be due to invalid IL or missing references)
				//IL_01f4: Unknown result type (might be due to invalid IL or missing references)
				//IL_0206: Unknown result type (might be due to invalid IL or missing references)
				//IL_0218: Unknown result type (might be due to invalid IL or missing references)
				//IL_022a: Unknown result type (might be due to invalid IL or missing references)
				//IL_023c: Unknown result type (might be due to invalid IL or missing references)
				//IL_0259: Unknown result type (might be due to invalid IL or missing references)
				//IL_026b: Unknown result type (might be due to invalid IL or missing references)
				//IL_0284: Unknown result type (might be due to invalid IL or missing references)
				//IL_0289: Unknown result type (might be due to invalid IL or missing references)
				//IL_0291: Unknown result type (might be due to invalid IL or missing references)
				//IL_0298: Unknown result type (might be due to invalid IL or missing references)
				//IL_02a2: Expected O, but got Unknown
				//IL_02a2: 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_02b1: Expected O, but got Unknown
				//IL_02b6: Expected O, but got Unknown
				//IL_02cb: Unknown result type (might be due to invalid IL or missing references)
				//IL_02d5: Expected O, but got Unknown
				//IL_02ef: Unknown result type (might be due to invalid IL or missing references)
				//IL_02f4: Unknown result type (might be due to invalid IL or missing references)
				//IL_02fc: Unknown result type (might be due to invalid IL or missing references)
				//IL_0304: Unknown result type (might be due to invalid IL or missing references)
				//IL_030b: Unknown result type (might be due to invalid IL or missing references)
				//IL_0312: Unknown result type (might be due to invalid IL or missing references)
				//IL_0317: Unknown result type (might be due to invalid IL or missing references)
				//IL_0321: Expected O, but got Unknown
				//IL_0326: Expected O, but got Unknown
				//IL_03ad: Unknown result type (might be due to invalid IL or missing references)
				if (!((Object)(object)_panelTex != (Object)null) || Panel == null)
				{
					_panelTex = Tex(new Color(0.07f, 0.06f, 0.05f, 0.97f));
					BarBack = Tex(new Color(0.2f, 0.18f, 0.15f));
					BarFill = Tex(Gold);
					Texture2D background = Tex(new Color(0.15f, 0.13f, 0.11f, 0.95f));
					Texture2D background2 = Tex(new Color(0.27f, 0.2f, 0.08f, 0.97f));
					Texture2D background3 = Tex(new Color(0.3f, 0.23f, 0.1f));
					Texture2D background4 = Tex(new Color(0.45f, 0.33f, 0.12f));
					Texture2D background5 = Tex(new Color(0.6f, 0.44f, 0.15f));
					RectOffset border = new RectOffset(0, 0, 0, 0);
					Panel = new GUIStyle(GUI.skin.window)
					{
						border = border,
						padding = new RectOffset(18, 18, 16, 16)
					};
					GUIStyleState[] array = (GUIStyleState[])(object)new GUIStyleState[8] { Panel.normal, Panel.onNormal, Panel.hover, Panel.onHover, Panel.active, Panel.onActive, Panel.focused, Panel.onFocused };
					for (int i = 0; i < array.Length; i++)
					{
						array[i].background = _panelTex;
					}
					Title = Label(26, (FontStyle)1, Gold);
					Subtitle = Label(14, (FontStyle)0, Text);
					Warning = Label(13, (FontStyle)2, new Color(0.95f, 0.55f, 0.4f));
					Message = Label(14, (FontStyle)1, Gold);
					Name = Label(18, (FontStyle)1, Text);
					Level = Label(16, (FontStyle)1, Gold);
					ActiveTag = Label(13, (FontStyle)1, Gold);
					ActiveTag.alignment = (TextAnchor)4;
					Small = Label(13, (FontStyle)0, Muted);
					Perk = Label(13, (FontStyle)0, Text);
					Row = new GUIStyle(GUI.skin.box)
					{
						border = border,
						padding = new RectOffset(12, 12, 8, 8),
						margin = new RectOffset(0, 6, 0, 6)
					};
					Row.normal.background = background;
					RowActive = new GUIStyle(Row);
					RowActive.normal.background = background2;
					Button = new GUIStyle(GUI.skin.button)
					{
						border = border,
						fontSize = 14,
						fontStyle = (FontStyle)1,
						alignment = (TextAnchor)4,
						padding = new RectOffset(8, 8, 6, 6)
					};
					Button.normal.background = background3;
					Button.hover.background = background4;
					Button.active.background = background5;
					Button.focused.background = background3;
					array = (GUIStyleState[])(object)new GUIStyleState[4] { Button.normal, Button.hover, Button.active, Button.focused };
					for (int i = 0; i < array.Length; i++)
					{
						array[i].textColor = Text;
					}
				}
			}

			private static GUIStyle Label(int size, FontStyle fontStyle, Color color)
			{
				//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_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)
				//IL_001d: 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_002a: Unknown result type (might be due to invalid IL or missing references)
				//IL_0031: Expected O, but got Unknown
				GUIStyle val = new GUIStyle(GUI.skin.label)
				{
					fontSize = size,
					fontStyle = fontStyle,
					wordWrap = true
				};
				val.normal.textColor = color;
				return val;
			}
		}

		private static readonly int WindowId = "com.danle.professions".GetHashCode();

		private const float WindowWidth = 580f;

		private const float MaxWindowHeight = 680f;

		private bool _visible;

		private bool _blocking;

		private Rect _window;

		private Vector2 _scroll;

		private string _message = "";

		private float _spawnedAt = -1f;

		private bool _hintShown;

		private void Update()
		{
			//IL_00a9: 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)
			Player localPlayer = Player.m_localPlayer;
			if ((Object)(object)localPlayer == (Object)null)
			{
				if (_visible)
				{
					Close();
				}
				_spawnedAt = -1f;
				_hintShown = false;
				return;
			}
			if (_spawnedAt < 0f)
			{
				_spawnedAt = Time.time;
			}
			if (!_hintShown && Time.time - _spawnedAt > 6f)
			{
				_hintShown = true;
				if (Plugin.ShowHint.Value && !ProfessionManager.GetActive(localPlayer).HasValue)
				{
					((Character)localPlayer).Message((MessageType)2, "Press " + KeyLabel() + " to choose a profession", 0, (Sprite)null, false);
				}
			}
			KeyboardShortcut value = Plugin.MenuKey.Value;
			if (((KeyboardShortcut)(ref value)).IsDown())
			{
				if (_visible)
				{
					Close();
				}
				else if (CanOpen())
				{
					Open();
				}
			}
			else if (_visible && (Input.GetKeyDown((KeyCode)27) || ((Character)localPlayer).IsDead()))
			{
				Close();
			}
		}

		private void OnDestroy()
		{
			if (_blocking)
			{
				_blocking = false;
				GUIManager.BlockInput(false);
			}
		}

		private static bool CanOpen()
		{
			if (!Console.IsVisible() && (!((Object)(object)Chat.instance != (Object)null) || !Chat.instance.HasFocus()) && !TextInput.IsVisible() && !Menu.IsVisible() && !InventoryGui.IsVisible() && !Minimap.IsOpen() && !StoreGui.IsVisible() && (!((Object)(object)TextViewer.instance != (Object)null) || !TextViewer.instance.IsVisible()))
			{
				return !Hud.IsPieceSelectionVisible();
			}
			return false;
		}

		private void Open()
		{
			_visible = true;
			_message = "";
			if (!_blocking)
			{
				_blocking = true;
				GUIManager.BlockInput(true);
			}
		}

		private void Close()
		{
			_visible = false;
			if (_blocking)
			{
				_blocking = false;
				((MonoBehaviour)this).StartCoroutine(ReleaseNextFrame());
			}
		}

		private static IEnumerator ReleaseNextFrame()
		{
			yield return null;
			GUIManager.BlockInput(false);
		}

		private static string KeyLabel()
		{
			//IL_0005: 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_0021: 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_0051: Unknown result type (might be due to invalid IL or missing references)
			//IL_0056: Unknown result type (might be due to invalid IL or missing references)
			KeyboardShortcut value = Plugin.MenuKey.Value;
			List<string> list = new List<string>();
			foreach (KeyCode modifier in ((KeyboardShortcut)(ref value)).Modifiers)
			{
				list.Add(((object)modifier/*cast due to .constrained prefix*/).ToString());
			}
			list.Add(((object)((KeyboardShortcut)(ref value)).MainKey/*cast due to .constrained prefix*/).ToString());
			return string.Join(" + ", list);
		}

		private void OnGUI()
		{
			//IL_0050: 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_013e: Unknown result type (might be due to invalid IL or missing references)
			//IL_014a: Unknown result type (might be due to invalid IL or missing references)
			//IL_015e: Expected O, but got Unknown
			//IL_0159: Unknown result type (might be due to invalid IL or missing references)
			//IL_015e: 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_00c6: Unknown result type (might be due to invalid IL or missing references)
			Player player = Player.m_localPlayer;
			if (_visible && !((Object)(object)player == (Object)null))
			{
				Styles.Ensure();
				float num = Mathf.Clamp((float)Screen.height / 1080f, 0.75f, 2.5f);
				Matrix4x4 matrix = GUI.matrix;
				GUI.matrix = Matrix4x4.Scale(new Vector3(num, num, 1f));
				float num2 = (float)Screen.width / num;
				float num3 = (float)Screen.height / num;
				float num4 = Mathf.Min(680f, num3 - 40f);
				if (((Rect)(ref _window)).width < 1f)
				{
					_window = new Rect((num2 - 580f) / 2f, (num3 - num4) / 2f, 580f, num4);
				}
				((Rect)(ref _window)).height = num4;
				((Rect)(ref _window)).x = Mathf.Clamp(((Rect)(ref _window)).x, 0f, Mathf.Max(0f, num2 - 580f));
				((Rect)(ref _window)).y = Mathf.Clamp(((Rect)(ref _window)).y, 0f, Mathf.Max(0f, num3 - num4));
				_window = GUI.Window(WindowId, _window, (WindowFunction)delegate
				{
					DrawWindow(player);
				}, GUIContent.none, Styles.Panel);
				GUI.matrix = matrix;
			}
		}

		private void DrawWindow(Player player)
		{
			//IL_008a: 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_019e: Unknown result type (might be due to invalid IL or missing references)
			GUILayout.Label("Professions", Styles.Title, Array.Empty<GUILayoutOption>());
			ProfessionType? active = ProfessionManager.GetActive(player);
			GUILayout.Label((!active.HasValue) ? "Choose a profession. Only your active one earns XP and gives its perk." : ("Active: " + ProfessionInfo.DisplayName(active.Value) + ". Other professions keep their XP while you're away."), Styles.Subtitle, Array.Empty<GUILayoutOption>());
			if (!Plugin.AllowSwitching.Value && active.HasValue)
			{
				GUILayout.Label("Switching professions is disabled on this server.", Styles.Warning, Array.Empty<GUILayoutOption>());
			}
			GUILayout.Space(6f);
			_scroll = GUILayout.BeginScrollView(_scroll, false, false, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.ExpandHeight(true) });
			ProfessionType[] array = (ProfessionType[])Enum.GetValues(typeof(ProfessionType));
			foreach (ProfessionType professionType in array)
			{
				DrawRow(player, professionType, active == professionType);
			}
			GUILayout.EndScrollView();
			if (_message.Length > 0)
			{
				GUILayout.Space(4f);
				GUILayout.Label(_message, Styles.Message, Array.Empty<GUILayoutOption>());
			}
			GUILayout.Space(6f);
			GUILayout.BeginHorizontal(Array.Empty<GUILayoutOption>());
			GUILayout.Label(KeyLabel() + " or Esc to close", Styles.Small, Array.Empty<GUILayoutOption>());
			GUILayout.FlexibleSpace();
			if (GUILayout.Button("Close", Styles.Button, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(90f) }))
			{
				Close();
			}
			GUILayout.EndHorizontal();
			GUI.DragWindow(new Rect(0f, 0f, 10000f, 44f));
		}

		private void DrawRow(Player player, ProfessionType type, bool isActive)
		{
			//IL_0195: Unknown result type (might be due to invalid IL or missing references)
			//IL_019a: Unknown result type (might be due to invalid IL or missing references)
			//IL_01a1: Unknown result type (might be due to invalid IL or missing references)
			//IL_01a7: Invalid comparison between Unknown and I4
			//IL_01a9: Unknown result type (might be due to invalid IL or missing references)
			//IL_01d4: Unknown result type (might be due to invalid IL or missing references)
			int level = ProfessionManager.GetLevel(player, type);
			long xp = ProfessionManager.GetXp(player, type);
			bool flag = level >= Plugin.MaxLevel.Value;
			long num = ProfessionManager.XpForLevel(level);
			long num2 = ProfessionManager.XpForLevel(level + 1);
			float num3 = ((flag || num2 <= num) ? 1f : Mathf.Clamp01((float)(xp - num) / (float)(num2 - num)));
			GUILayout.BeginVertical(isActive ? Styles.RowActive : Styles.Row, Array.Empty<GUILayoutOption>());
			GUILayout.BeginHorizontal(Array.Empty<GUILayoutOption>());
			GUILayout.Label(ProfessionInfo.DisplayName(type), Styles.Name, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(170f) });
			GUILayout.Label($"Lv {level}", Styles.Level, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(60f) });
			GUILayout.FlexibleSpace();
			if (isActive)
			{
				GUILayout.Label("ACTIVE", Styles.ActiveTag, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(90f) });
			}
			else
			{
				bool enabled = GUI.enabled;
				GUI.enabled = ProfessionManager.CanChoose(player, type);
				if (GUILayout.Button("Choose", Styles.Button, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(90f) }))
				{
					Choose(player, type);
				}
				GUI.enabled = enabled;
			}
			GUILayout.EndHorizontal();
			GUILayout.Label(ProfessionInfo.Summary(type), Styles.Small, Array.Empty<GUILayoutOption>());
			GUILayout.Label($"Perk: +{ProfessionManager.BonusPercent(player, type):0.#}% {ProfessionInfo.Perk(type)}", Styles.Perk, Array.Empty<GUILayoutOption>());
			Rect rect = GUILayoutUtility.GetRect(1f, 8f, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.ExpandWidth(true) });
			if ((int)Event.current.type == 7)
			{
				GUI.DrawTexture(rect, (Texture)(object)Styles.BarBack);
				GUI.DrawTexture(new Rect(((Rect)(ref rect)).x, ((Rect)(ref rect)).y, ((Rect)(ref rect)).width * num3, ((Rect)(ref rect)).height), (Texture)(object)Styles.BarFill);
			}
			GUILayout.Label(flag ? $"{xp} XP (max level)" : $"{xp} / {num2} XP", Styles.Small, Array.Empty<GUILayoutOption>());
			GUILayout.EndVertical();
		}

		private void Choose(Player player, ProfessionType type)
		{
			if (ProfessionManager.TryChoose(player, type, out var error))
			{
				_message = "You are now a " + ProfessionInfo.DisplayName(type) + ".";
				((Character)player).Message((MessageType)1, _message, 0, (Sprite)null, false);
			}
			else
			{
				_message = error;
			}
		}
	}
	public enum ProfessionType
	{
		Miner,
		Woodcutter,
		Hunter,
		Farmer,
		Blacksmith,
		Alchemist,
		Mason,
		Mariner,
		TavernKeeper
	}
	public static class ProfessionInfo
	{
		public static bool IsDamagePerk(ProfessionType type)
		{
			if (type != ProfessionType.Miner && type != ProfessionType.Woodcutter)
			{
				return type == ProfessionType.Hunter;
			}
			return true;
		}

		public static bool SavesMaterials(ProfessionType type)
		{
			if (type != ProfessionType.Blacksmith && type != ProfessionType.Alchemist)
			{
				return type == ProfessionType.Mason;
			}
			return true;
		}

		public static string DisplayName(ProfessionType type)
		{
			if (type != ProfessionType.TavernKeeper)
			{
				return type.ToString();
			}
			return "Tavern Keeper";
		}

		public static string Summary(ProfessionType type)
		{
			return type switch
			{
				ProfessionType.Miner => "Mining rock and ore with a pickaxe", 
				ProfessionType.Woodcutter => "Chopping trees and logs", 
				ProfessionType.Hunter => "Fighting creatures and monsters", 
				ProfessionType.Farmer => "Harvesting crops and forage, and planting", 
				ProfessionType.Blacksmith => "Crafting and upgrading at forges", 
				ProfessionType.Alchemist => "Brewing meads and potions", 
				ProfessionType.Mason => "Stonework: stone building and the stonecutter", 
				ProfessionType.Mariner => "Sailing ships and fishing", 
				ProfessionType.TavernKeeper => "Cooking: crafting food and using cooking stations", 
				_ => "", 
			};
		}

		public static string Perk(ProfessionType type)
		{
			return type switch
			{
				ProfessionType.Miner => "pickaxe damage", 
				ProfessionType.Woodcutter => "axe chop damage", 
				ProfessionType.Hunter => "damage against creatures", 
				ProfessionType.Farmer => "chance of a double harvest", 
				ProfessionType.Blacksmith => "chance to save materials at forges", 
				ProfessionType.Alchemist => "chance to save materials when brewing meads and potions", 
				ProfessionType.Mason => "chance to save materials on stone building and stonecutting", 
				ProfessionType.Mariner => "sail speed", 
				ProfessionType.TavernKeeper => "longer-lasting food", 
				_ => "", 
			};
		}
	}
}
namespace System.Runtime.CompilerServices
{
	[AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)]
	internal sealed class IgnoresAccessChecksToAttribute : Attribute
	{
		public IgnoresAccessChecksToAttribute(string assemblyName)
		{
		}
	}
}