Decompiled source of BudylivSkillRates v1.1.1

plugins/BudylivSkillRates.dll

Decompiled a day 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 BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using Jotunn.Utils;
using Microsoft.CodeAnalysis;
using UnityEngine;

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

		public RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
}
namespace BudylivSkillRates
{
	public static class FoodXp
	{
		public static string Context;

		internal static ConfigEntry<bool> Enabled;

		internal static ConfigEntry<float> IngredientBonus;

		internal static ConfigEntry<float> TierBonus;

		internal static ConfigEntry<string> Tiers;

		internal const string DefaultTiers = "1:RawMeat,CookedMeat,DeerMeat,CookedDeerMeat,NeckTail,NeckTailGrilled,Honey,Raspberry,Mushroom,Dandelion,Fish1,FishRaw,FishCooked;2:Blueberries,Carrot,Thistle,MushroomYellow,GreydwarfEye,BjornMeat,CookedBjornMeat,Fish2,SpiceForests,CuredSquirrelHamstring;3:Turnip,Bloodbag,Entrails,Ooze,SerpentMeat,SerpentMeatCooked,Fish3,Fish5,Fish6,Fish8,Fish9,SpiceOceans,FreshSeaweed,TrophySerpent,TrophyAbomination;4:Onion,WolfMeat,CookedWolfMeat,FreezeGland,Fish4_cave,SpiceMountains,PowderedDragonEgg,TrophyFenring,TrophyHatchling;5:Barley,BarleyFlour,Cloudberry,LoxMeat,CookedLoxMeat,ChickenEgg,ChickenMeat,CookedChickenMeat,Fish7,SpicePlains,PungentPebbles,TrophyGoblin,TrophyLox;6:BugMeat,CookedBugMeat,HareMeat,CookedHareMeat,MushroomJotunPuffs,MushroomMagecap,RoyalJelly,Sap,GiantBloodSack,Fish12,SpiceMistlands;7:AsksvinMeat,CookedAsksvinMeat,VoltureMeat,CookedVoltureMeat,VoltureEgg,Vineberry,Fiddleheadfern,MushroomSmokePuff,Fish11,SpiceAshlands,ScaleHide,TrophyCharredMelee;8:Oat,OatFlour,OatMilk,Kale,Poteitr,Lingonberry,MooseMeat,CookedMooseMeat,SealBlubber,Ice,Fish10,SpiceDeepNorth,TrophyFrostTroll";

		private static Dictionary<string, int> _table = new Dictionary<string, int>();

		private static readonly Dictionary<string, (int Count, int Tier)> Cache = new Dictionary<string, (int, int)>();

		private static Dictionary<string, Recipe> _recipes;

		private static Dictionary<string, string> _cookedFrom;

		private static ObjectDB _indexedDb;

		internal static void Bind(ConfigFile config)
		{
			//IL_0020: Unknown result type (might be due to invalid IL or missing references)
			//IL_002a: Expected O, but got Unknown
			//IL_0061: Unknown result type (might be due to invalid IL or missing references)
			//IL_006b: Expected O, but got Unknown
			//IL_00a2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ac: Expected O, but got Unknown
			//IL_00d5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00df: Expected O, but got Unknown
			Enabled = config.Bind<bool>("Cooking", "FoodDependentXP", true, new ConfigDescription("Cooking XP depends on the dish: more distinct ingredients and later-game ingredients give more.", (AcceptableValueBase)null, new object[1] { Admin() }));
			IngredientBonus = config.Bind<float>("Cooking", "IngredientBonus", 0.25f, new ConfigDescription("Extra multiplier per distinct ingredient after the first (0.25: 3 ingredients = x1.5).", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 5f), new object[1] { Admin() }));
			TierBonus = config.Bind<float>("Cooking", "TierBonus", 0.5f, new ConfigDescription("Extra multiplier per ingredient tier above 1 (0.5: tier 8 = x4.5).", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 5f), new object[1] { Admin() }));
			Tiers = config.Bind<string>("Cooking", "IngredientTiers", "1:RawMeat,CookedMeat,DeerMeat,CookedDeerMeat,NeckTail,NeckTailGrilled,Honey,Raspberry,Mushroom,Dandelion,Fish1,FishRaw,FishCooked;2:Blueberries,Carrot,Thistle,MushroomYellow,GreydwarfEye,BjornMeat,CookedBjornMeat,Fish2,SpiceForests,CuredSquirrelHamstring;3:Turnip,Bloodbag,Entrails,Ooze,SerpentMeat,SerpentMeatCooked,Fish3,Fish5,Fish6,Fish8,Fish9,SpiceOceans,FreshSeaweed,TrophySerpent,TrophyAbomination;4:Onion,WolfMeat,CookedWolfMeat,FreezeGland,Fish4_cave,SpiceMountains,PowderedDragonEgg,TrophyFenring,TrophyHatchling;5:Barley,BarleyFlour,Cloudberry,LoxMeat,CookedLoxMeat,ChickenEgg,ChickenMeat,CookedChickenMeat,Fish7,SpicePlains,PungentPebbles,TrophyGoblin,TrophyLox;6:BugMeat,CookedBugMeat,HareMeat,CookedHareMeat,MushroomJotunPuffs,MushroomMagecap,RoyalJelly,Sap,GiantBloodSack,Fish12,SpiceMistlands;7:AsksvinMeat,CookedAsksvinMeat,VoltureMeat,CookedVoltureMeat,VoltureEgg,Vineberry,Fiddleheadfern,MushroomSmokePuff,Fish11,SpiceAshlands,ScaleHide,TrophyCharredMelee;8:Oat,OatFlour,OatMilk,Kale,Poteitr,Lingonberry,MooseMeat,CookedMooseMeat,SealBlubber,Ice,Fish10,SpiceDeepNorth,TrophyFrostTroll", new ConfigDescription("Ingredient tiers: 'tier:Prefab,Prefab;tier:...' (1 Meadows, 2 Black Forest, 3 Swamp/Ocean, 4 Mountains, 5 Plains, 6 Mistlands, 7 Ashlands, 8 Deep North). Unlisted crafted foods take the highest tier of their ingredients, other items tier 1.", (AcceptableValueBase)null, new object[1] { Admin() }));
			ParseTable();
			Tiers.SettingChanged += delegate
			{
				ParseTable();
			};
			IngredientBonus.SettingChanged += delegate
			{
				Cache.Clear();
			};
			TierBonus.SettingChanged += delegate
			{
				Cache.Clear();
			};
			static ConfigurationManagerAttributes Admin()
			{
				//IL_0000: 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)
				//IL_000d: Expected O, but got Unknown
				return new ConfigurationManagerAttributes
				{
					IsAdminOnly = true
				};
			}
		}

		private static void ParseTable()
		{
			Dictionary<string, int> dictionary = new Dictionary<string, int>();
			string[] array = (Tiers.Value ?? "").Split(';');
			foreach (string text in array)
			{
				int num = text.IndexOf(':');
				if (num <= 0 || !int.TryParse(text.Substring(0, num).Trim(), NumberStyles.Integer, CultureInfo.InvariantCulture, out var result))
				{
					continue;
				}
				string[] array2 = text.Substring(num + 1).Split(',');
				foreach (string text2 in array2)
				{
					if (text2.Trim().Length > 0)
					{
						dictionary[text2.Trim()] = Mathf.Clamp(result, 1, 20);
					}
				}
			}
			_table = dictionary;
			Cache.Clear();
		}

		private static bool EnsureIndex()
		{
			//IL_0092: Unknown result type (might be due to invalid IL or missing references)
			//IL_0099: Invalid comparison between Unknown and I4
			if ((Object)(object)ObjectDB.instance == (Object)null || (Object)(object)ZNetScene.instance == (Object)null)
			{
				return false;
			}
			if (_recipes != null && _indexedDb == ObjectDB.instance)
			{
				return true;
			}
			_indexedDb = ObjectDB.instance;
			_recipes = new Dictionary<string, Recipe>();
			foreach (Recipe recipe in ObjectDB.instance.m_recipes)
			{
				if (!((Object)(object)recipe == (Object)null) && !((Object)(object)recipe.m_item == (Object)null) && recipe.m_enabled && !((Object)(object)recipe.m_craftingStation == (Object)null) && (int)recipe.m_craftingStation.m_craftingSkill == 105)
				{
					string name = ((Object)((Component)recipe.m_item).gameObject).name;
					if (!_recipes.ContainsKey(name))
					{
						_recipes[name] = recipe;
					}
				}
			}
			_cookedFrom = new Dictionary<string, string>();
			foreach (GameObject prefab in ZNetScene.instance.m_prefabs)
			{
				CookingStation val = (((Object)(object)prefab != (Object)null) ? prefab.GetComponent<CookingStation>() : null);
				if ((Object)(object)val == (Object)null || val.m_conversion == null)
				{
					continue;
				}
				foreach (ItemConversion item in val.m_conversion)
				{
					if ((Object)(object)item?.m_from != (Object)null && (Object)(object)item.m_to != (Object)null && !_cookedFrom.ContainsKey(((Object)((Component)item.m_to).gameObject).name))
					{
						_cookedFrom[((Object)((Component)item.m_to).gameObject).name] = ((Object)((Component)item.m_from).gameObject).name;
					}
				}
			}
			Cache.Clear();
			Plugin.Log.LogInfo((object)$"Food XP: {_recipes.Count} cooking recipes, {_cookedFrom.Count} cooking station products, {_table.Count} items in the tier table.");
			return true;
		}

		private static IEnumerable<string> Ingredients(Recipe r)
		{
			return (from q in r.m_resources
				where q != null && (Object)(object)q.m_resItem != (Object)null && q.m_amount > 0
				select ((Object)((Component)q.m_resItem).gameObject).name).Distinct();
		}

		private static int Tier(string name, int depth)
		{
			if (_table.TryGetValue(name, out var value))
			{
				return value;
			}
			if (depth > 8)
			{
				return 1;
			}
			if (_recipes.TryGetValue(name, out var value2))
			{
				List<int> list = (from i in Ingredients(value2)
					select Tier(i, depth + 1)).ToList();
				if (list.Count == 0)
				{
					return 1;
				}
				if (!value2.m_requireOnlyOneIngredient)
				{
					return list.Max();
				}
				return list.Min();
			}
			if (_cookedFrom.TryGetValue(name, out var value3))
			{
				return Tier(value3, depth + 1);
			}
			return 1;
		}

		private static (int Count, int Tier) Complexity(string name, int depth)
		{
			if (Cache.TryGetValue(name, out (int, int) value))
			{
				return value;
			}
			Recipe value2;
			string value3;
			(int, int) tuple = (_recipes.TryGetValue(name, out value2) ? (value2.m_requireOnlyOneIngredient ? 1 : Math.Max(1, Ingredients(value2).Count()), Tier(name, 0)) : ((depth >= 8 || !_cookedFrom.TryGetValue(name, out value3)) ? (1, Tier(name, 0)) : Complexity(value3, depth + 1)));
			Cache[name] = tuple;
			return tuple;
		}

		internal static float Multiplier(string prefab)
		{
			if (string.IsNullOrEmpty(prefab) || Enabled == null || !Enabled.Value || !EnsureIndex())
			{
				return 1f;
			}
			var (num, num2) = Complexity(prefab, 0);
			return (1f + IngredientBonus.Value * (float)(num - 1)) * (1f + TierBonus.Value * (float)(num2 - 1));
		}

		public static void RaiseCooking(string prefab, float amount)
		{
			if ((Object)(object)Player.m_localPlayer == (Object)null)
			{
				return;
			}
			Context = prefab;
			try
			{
				((Character)Player.m_localPlayer).RaiseSkill((SkillType)105, amount);
			}
			finally
			{
				Context = null;
			}
		}
	}
	[HarmonyPatch(typeof(InventoryGui), "DoCrafting")]
	internal static class DoCraftingPatch
	{
		private static readonly FieldRef<InventoryGui, Recipe> CraftRecipe = AccessTools.FieldRefAccess<InventoryGui, Recipe>("m_craftRecipe");

		private static void Prefix(InventoryGui __instance)
		{
			Recipe val = CraftRecipe.Invoke(__instance);
			FoodXp.Context = (((Object)(object)val != (Object)null && (Object)(object)val.m_item != (Object)null) ? ((Object)((Component)val.m_item).gameObject).name : null);
		}

		private static void Finalizer()
		{
			FoodXp.Context = null;
		}
	}
	[HarmonyPatch(typeof(CookingStation), "OnInteract")]
	internal static class CookingInteractPatch
	{
		private static readonly FieldRef<CookingStation, ZNetView> NView = AccessTools.FieldRefAccess<CookingStation, ZNetView>("m_nview");

		private static readonly Func<CookingStation, bool> HaveDoneItem = AccessTools.MethodDelegate<Func<CookingStation, bool>>(AccessTools.Method(typeof(CookingStation), "HaveDoneItem", (Type[])null, (Type[])null), (object)null, true);

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

		private static readonly Func<CookingStation, Inventory, ItemData> FindCookableItem = AccessTools.MethodDelegate<Func<CookingStation, Inventory, ItemData>>(AccessTools.Method(typeof(CookingStation), "FindCookableItem", (Type[])null, (Type[])null), (object)null, true);

		private static void Prefix(CookingStation __instance, Humanoid user)
		{
			FoodXp.Context = null;
			try
			{
				ZNetView obj = NView.Invoke(__instance);
				ZDO val = ((obj != null) ? obj.GetZDO() : null);
				if (val == null)
				{
					return;
				}
				if (HaveDoneItem(__instance))
				{
					for (int i = 0; i < __instance.m_slots.Length; i++)
					{
						string text = val.GetString("slot" + i, "");
						if (text != "" && IsItemDone(__instance, text))
						{
							FoodXp.Context = text;
							break;
						}
					}
				}
				else if ((Object)(object)user != (Object)null)
				{
					ItemData obj2 = FindCookableItem(__instance, user.GetInventory());
					object context;
					if (obj2 == null)
					{
						context = null;
					}
					else
					{
						GameObject dropPrefab = obj2.m_dropPrefab;
						context = ((dropPrefab != null) ? ((Object)dropPrefab).name : null);
					}
					FoodXp.Context = (string)context;
				}
			}
			catch (Exception ex)
			{
				Plugin.Log.LogWarning((object)("Food XP: cooking station lookup failed: " + ex.Message));
			}
		}

		private static void Finalizer()
		{
			FoodXp.Context = null;
		}
	}
	[BepInPlugin("com.budyliv.skillrates", "Budyliv Skill Rates", "1.1.1")]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	[NetworkCompatibility(/*Could not decode attribute arguments.*/)]
	public class Plugin : BaseUnityPlugin
	{
		public const string Guid = "com.budyliv.skillrates";

		public const string Name = "Budyliv Skill Rates";

		public const string Version = "1.1.1";

		internal static readonly Dictionary<SkillType, ConfigEntry<float>> Rates = new Dictionary<SkillType, ConfigEntry<float>>();

		internal static ConfigEntry<bool> CarryOver;

		internal static ManualLogSource Log;

		private static readonly HashSet<SkillType> Fast = new HashSet<SkillType>
		{
			(SkillType)10,
			(SkillType)108,
			(SkillType)103,
			(SkillType)107,
			(SkillType)12,
			(SkillType)13
		};

		private void Awake()
		{
			//IL_002a: 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_0037: Expected O, but got Unknown
			//IL_0037: Unknown result type (might be due to invalid IL or missing references)
			//IL_0041: Expected O, but got Unknown
			//IL_0066: 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_0072: Unknown result type (might be due to invalid IL or missing references)
			//IL_0078: Invalid comparison between Unknown and I4
			//IL_0125: Unknown result type (might be due to invalid IL or missing references)
			//IL_012a: Unknown result type (might be due to invalid IL or missing references)
			//IL_013a: Unknown result type (might be due to invalid IL or missing references)
			//IL_007a: Unknown result type (might be due to invalid IL or missing references)
			//IL_007d: Invalid comparison between Unknown and I4
			//IL_0084: 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_00db: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e8: Expected O, but got Unknown
			//IL_00e8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f2: Expected O, but got Unknown
			Log = ((BaseUnityPlugin)this).Logger;
			CarryOver = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "CarryOverXP", true, new ConfigDescription("Carry XP left over after a level-up to the next level (only for skills whose rate is not 1). Vanilla throws it away.", (AcceptableValueBase)null, new object[1] { (object)new ConfigurationManagerAttributes
			{
				IsAdminOnly = true
			} }));
			foreach (SkillType value in Enum.GetValues(typeof(SkillType)))
			{
				if ((int)value != 0 && (int)value != 999)
				{
					float num = (((int)value == 105) ? 1f : (Fast.Contains(value) ? 10f : 3f));
					Rates[value] = ((BaseUnityPlugin)this).Config.Bind<float>("Rates", ((object)value/*cast due to .constrained prefix*/).ToString(), num, new ConfigDescription("XP multiplier for this skill (1 = vanilla, 0 = no XP). Applied on top of the world skill gain modifier.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 100f), new object[1] { (object)new ConfigurationManagerAttributes
					{
						IsAdminOnly = true
					} }));
				}
			}
			FoodXp.Bind(((BaseUnityPlugin)this).Config);
			Harmony val2 = new Harmony("com.budyliv.skillrates");
			val2.PatchAll(typeof(RaiseSkillPatch));
			val2.PatchAll(typeof(DoCraftingPatch));
			val2.PatchAll(typeof(CookingInteractPatch));
			((BaseUnityPlugin)this).Logger.LogInfo((object)"Budyliv Skill Rates 1.1.1 loaded");
		}
	}
	[HarmonyPatch(typeof(Skills), "RaiseSkill")]
	internal static class RaiseSkillPatch
	{
		private static readonly Func<Skills, SkillType, Skill> GetSkill = AccessTools.MethodDelegate<Func<Skills, SkillType, Skill>>(AccessTools.Method(typeof(Skills), "GetSkill", (Type[])null, (Type[])null), (object)null, true);

		private static bool _inside;

		[HarmonyPriority(200)]
		private static bool Prefix(Skills __instance, SkillType skillType, ref float factor)
		{
			//IL_0009: Unknown result type (might be due to invalid IL or missing references)
			//IL_000c: Invalid comparison between Unknown and I4
			//IL_0029: 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_012a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0106: Unknown result type (might be due to invalid IL or missing references)
			if (_inside)
			{
				return true;
			}
			if ((int)skillType == 105 && FoodXp.Context != null)
			{
				factor *= FoodXp.Multiplier(FoodXp.Context);
			}
			if (!Plugin.Rates.TryGetValue(skillType, out var value))
			{
				return true;
			}
			float value2 = value.Value;
			if (Mathf.Approximately(value2, 1f))
			{
				return true;
			}
			if (value2 <= 0f)
			{
				return false;
			}
			if (factor <= 0f || !Plugin.CarryOver.Value)
			{
				factor *= value2;
				return true;
			}
			Skill val = GetSkill(__instance, skillType);
			float num = val.m_info.m_increseStep * Game.m_skillGainRate;
			if (num <= 0f)
			{
				factor *= value2;
				return true;
			}
			float num2 = num * factor * value2;
			_inside = true;
			try
			{
				for (int i = 0; i < 101; i++)
				{
					if (!(num2 > 1E-06f))
					{
						break;
					}
					if (!(val.m_level < 100f))
					{
						break;
					}
					float num3 = Mathf.Pow(Mathf.Floor(val.m_level + 1f), 1.5f) * 0.5f + 0.5f - val.m_accumulator;
					if (num3 < 0.0001f)
					{
						num3 = 0.0001f;
					}
					if (num2 >= num3)
					{
						float level = val.m_level;
						__instance.RaiseSkill(skillType, num3 * 1.0001f / num);
						num2 -= num3;
						if (val.m_level <= level)
						{
							break;
						}
					}
					else
					{
						__instance.RaiseSkill(skillType, num2 / num);
						num2 = 0f;
					}
				}
			}
			finally
			{
				_inside = false;
			}
			return false;
		}
	}
}