Decompiled source of BalancedIdolRefinement v1.0.0

BalancedIdolRefinement.dll

Decompiled 2 days ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Reflection;
using System.Reflection.Emit;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Permissions;
using BepInEx;
using BepInEx.Logging;
using HarmonyLib;
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: AssemblyCompany("BalancedIdolRefinement")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0")]
[assembly: AssemblyProduct("BalancedIdolRefinement")]
[assembly: AssemblyTitle("BalancedIdolRefinement")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("1.0.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 BalancedIdolRefinement
{
	[BepInPlugin("com.valheim.balancedidolrefinement", "BalancedIdolRefinement", "1.0.0")]
	public class BalancedIdolRefinementPlugin : BaseUnityPlugin
	{
		internal sealed class IdolRecipeSpec
		{
			public readonly string IdolPrefab;

			public readonly string[] StationCandidates;

			public readonly int MinStationLevel;

			public readonly (string[] MaterialCandidates, int Amount)[] Ingredients;

			public IdolRecipeSpec(string idolPrefab, string[] stationCandidates, int minStationLevel, params (string[] MaterialCandidates, int Amount)[] ingredients)
			{
				IdolPrefab = idolPrefab;
				StationCandidates = stationCandidates;
				MinStationLevel = minStationLevel;
				Ingredients = ingredients;
			}
		}

		public const string PluginGuid = "com.valheim.balancedidolrefinement";

		public const string PluginName = "BalancedIdolRefinement";

		public const string PluginVersion = "1.0.0";

		internal static ManualLogSource Log;

		private Harmony _harmony;

		private static readonly string[] WorkbenchStations = new string[2] { "piece_workbench", "$piece_workbench" };

		private static readonly string[] ForgeStations = new string[3] { "forge", "piece_forge", "$piece_forge" };

		private static readonly string[] BlackForgeStations = new string[3] { "piece_blackforge", "blackforge", "$piece_blackforge" };

		internal static readonly IdolRecipeSpec[] IdolSpecs = new IdolRecipeSpec[16]
		{
			new IdolRecipeSpec("Upgrader0Weapon", WorkbenchStations, 1, (new string[1] { "Wood" }, 30), (new string[1] { "Flint" }, 20), (new string[1] { "Resin" }, 15)),
			new IdolRecipeSpec("Upgrader0Armor", WorkbenchStations, 1, (new string[1] { "Wood" }, 30), (new string[1] { "LeatherScraps" }, 20), (new string[1] { "DeerHide" }, 10)),
			new IdolRecipeSpec("Upgrader1Weapon", ForgeStations, 1, (new string[1] { "Bronze" }, 20), (new string[1] { "RoundLog" }, 15), (new string[1] { "GreydwarfEye" }, 15)),
			new IdolRecipeSpec("Upgrader1Armor", ForgeStations, 1, (new string[1] { "Bronze" }, 20), (new string[1] { "TrollHide" }, 10), (new string[1] { "GreydwarfEye" }, 15)),
			new IdolRecipeSpec("Upgrader2Weapon", ForgeStations, 2, (new string[1] { "Iron" }, 20), (new string[1] { "ElderBark" }, 15), (new string[1] { "Guck" }, 10)),
			new IdolRecipeSpec("Upgrader2Armor", ForgeStations, 2, (new string[1] { "Iron" }, 20), (new string[1] { "Root" }, 8), (new string[1] { "Bloodbag" }, 15)),
			new IdolRecipeSpec("Upgrader3Weapon", ForgeStations, 3, (new string[1] { "Silver" }, 20), (new string[1] { "WolfFang" }, 12), (new string[1] { "FreezeGland" }, 8)),
			new IdolRecipeSpec("Upgrader3Armor", ForgeStations, 3, (new string[1] { "Silver" }, 20), (new string[1] { "WolfPelt" }, 12), (new string[1] { "Crystal" }, 8)),
			new IdolRecipeSpec("Upgrader4Weapon", ForgeStations, 4, (new string[1] { "BlackMetal" }, 20), (new string[1] { "Needle" }, 15), (new string[1] { "LinenThread" }, 15)),
			new IdolRecipeSpec("Upgrader4Armor", ForgeStations, 4, (new string[1] { "BlackMetal" }, 20), (new string[1] { "LoxPelt" }, 10), (new string[1] { "LinenThread" }, 15)),
			new IdolRecipeSpec("Upgrader5Weapon", BlackForgeStations, 1, (new string[1] { "BlackMarble" }, 30), (new string[1] { "Eitr" }, 15), (new string[1] { "Carapace" }, 15)),
			new IdolRecipeSpec("Upgrader5Armor", BlackForgeStations, 1, (new string[1] { "BlackMarble" }, 30), (new string[1] { "Eitr" }, 15), (new string[1] { "ScaleHide" }, 15)),
			new IdolRecipeSpec("Upgrader6Weapon", BlackForgeStations, 2, (new string[2] { "FlametalNew", "Flametal" }, 20), (new string[1] { "CharredBone" }, 15), (new string[1] { "ProustitePowder" }, 10)),
			new IdolRecipeSpec("Upgrader6Armor", BlackForgeStations, 2, (new string[2] { "FlametalNew", "Flametal" }, 20), (new string[1] { "AskHide" }, 12), (new string[1] { "CelestialFeather" }, 8)),
			new IdolRecipeSpec("Upgrader7Weapon", BlackForgeStations, 3, (new string[1] { "Gold" }, 20), (new string[1] { "Frostwood" }, 20), (new string[1] { "WolfClaw" }, 10)),
			new IdolRecipeSpec("Upgrader7Armor", BlackForgeStations, 3, (new string[1] { "Gold" }, 20), (new string[1] { "MooseHide" }, 12), (new string[1] { "Voidplasm" }, 10))
		};

		private void Awake()
		{
			//IL_0011: Unknown result type (might be due to invalid IL or missing references)
			//IL_001b: Expected O, but got Unknown
			Log = ((BaseUnityPlugin)this).Logger;
			_harmony = new Harmony("com.valheim.balancedidolrefinement");
			_harmony.PatchAll(typeof(BalancedIdolRefinementPlugin).Assembly);
			Log.LogInfo((object)"BalancedIdolRefinement 1.0.0 loaded.");
		}

		private void OnDestroy()
		{
			Harmony harmony = _harmony;
			if (harmony != null)
			{
				harmony.UnpatchSelf();
			}
		}

		internal static void ConfigureIdolsAndRecipes(ObjectDB db)
		{
			if ((Object)(object)db == (Object)null || db.m_items == null || db.m_items.Count == 0 || db.m_recipes == null)
			{
				return;
			}
			int num = 0;
			int num2 = 0;
			IdolRecipeSpec[] idolSpecs = IdolSpecs;
			foreach (IdolRecipeSpec idolRecipeSpec in idolSpecs)
			{
				GameObject itemPrefab = db.GetItemPrefab(idolRecipeSpec.IdolPrefab);
				if ((Object)(object)itemPrefab == (Object)null)
				{
					continue;
				}
				ItemDrop component = itemPrefab.GetComponent<ItemDrop>();
				if ((Object)(object)component == (Object)null || component.m_itemData == null || component.m_itemData.m_shared == null)
				{
					continue;
				}
				SharedData shared = component.m_itemData.m_shared;
				shared.m_upgradeChance = 0.65f;
				shared.m_breakChance = 0f;
				shared.m_breakReturnIngreientsAmount = 0f;
				num++;
				string recipeName = "Recipe_" + idolRecipeSpec.IdolPrefab;
				Recipe val = FindExistingIdolRecipe(db, recipeName, component);
				CraftingStation station = FindCraftingStation(db, idolRecipeSpec.StationCandidates);
				Requirement[] array = BuildRequirements(db, idolRecipeSpec.Ingredients);
				if (array.Length != 0)
				{
					if ((Object)(object)val != (Object)null)
					{
						PopulateRecipe(val, recipeName, component, station, idolRecipeSpec.MinStationLevel, array);
						continue;
					}
					Recipe val2 = ScriptableObject.CreateInstance<Recipe>();
					PopulateRecipe(val2, recipeName, component, station, idolRecipeSpec.MinStationLevel, array);
					db.m_recipes.Add(val2);
					num2++;
				}
			}
			foreach (Recipe recipe in db.m_recipes)
			{
				if ((Object)(object)recipe == (Object)null || recipe.m_resources == null)
				{
					continue;
				}
				Requirement[] resources = recipe.m_resources;
				foreach (Requirement val3 in resources)
				{
					if (val3 != null && val3.m_upgraderResource && (Object)(object)val3.m_resItem != (Object)null && val3.m_resItem.m_itemData?.m_shared != null)
					{
						val3.m_resItem.m_itemData.m_shared.m_breakChance = 0f;
						val3.m_resItem.m_itemData.m_shared.m_breakReturnIngreientsAmount = 0f;
					}
				}
			}
			if (num > 0 || num2 > 0)
			{
				ManualLogSource log = Log;
				if (log != null)
				{
					log.LogInfo((object)$"Configured {num} Idols (0% equipment break, downgrade by 1 level on fail, 0% scrap refund) and registered {num2} Idol recipes.");
				}
			}
		}

		private static void PopulateRecipe(Recipe recipe, string recipeName, ItemDrop idolDrop, CraftingStation station, int minStationLevel, Requirement[] requirements)
		{
			((Object)recipe).name = recipeName;
			recipe.m_item = idolDrop;
			recipe.m_amount = 1;
			recipe.m_enabled = true;
			recipe.m_qualityResultAmountMultiplier = 1f;
			recipe.m_listSortWeight = 100;
			recipe.m_noCraftOnlyUpgrade = false;
			recipe.m_craftingStation = station;
			recipe.m_repairStation = station;
			recipe.m_minStationLevel = minStationLevel;
			recipe.m_requireOnlyOneIngredient = false;
			recipe.m_resources = requirements;
		}

		private static Recipe FindExistingIdolRecipe(ObjectDB db, string recipeName, ItemDrop idolDrop)
		{
			foreach (Recipe recipe in db.m_recipes)
			{
				if (!((Object)(object)recipe == (Object)null) && (string.Equals(((Object)recipe).name, recipeName, StringComparison.Ordinal) || ((Object)(object)recipe.m_item != (Object)null && recipe.m_item.m_itemData?.m_shared != null && idolDrop.m_itemData?.m_shared != null && string.Equals(((Object)recipe.m_item).name, ((Object)idolDrop).name, StringComparison.Ordinal) && !recipe.m_noCraftOnlyUpgrade)))
				{
					return recipe;
				}
			}
			return null;
		}

		private static Requirement[] BuildRequirements(ObjectDB db, (string[] MaterialCandidates, int Amount)[] ingredients)
		{
			//IL_00a5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00aa: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ba: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00cf: Unknown result type (might be due to invalid IL or missing references)
			//IL_00db: Expected O, but got Unknown
			List<Requirement> list = new List<Requirement>(ingredients.Length);
			for (int i = 0; i < ingredients.Length; i++)
			{
				(string[] MaterialCandidates, int Amount) tuple = ingredients[i];
				string[] item = tuple.MaterialCandidates;
				int item2 = tuple.Amount;
				ItemDrop val = null;
				string[] array = item;
				foreach (string text in array)
				{
					GameObject itemPrefab = db.GetItemPrefab(text);
					if ((Object)(object)itemPrefab != (Object)null)
					{
						val = itemPrefab.GetComponent<ItemDrop>();
						if ((Object)(object)val != (Object)null)
						{
							break;
						}
					}
				}
				if ((Object)(object)val == (Object)null)
				{
					ManualLogSource log = Log;
					if (log != null)
					{
						log.LogWarning((object)("Could not find material prefab (" + string.Join("/", item) + ") in ObjectDB."));
					}
				}
				else
				{
					list.Add(new Requirement
					{
						m_resItem = val,
						m_amount = item2,
						m_amountPerLevel = 0,
						m_extraAmountOnlyOneIngredient = 0,
						m_upgraderResource = false,
						m_recover = true
					});
				}
			}
			return list.ToArray();
		}

		private static CraftingStation FindCraftingStation(ObjectDB db, string[] candidates)
		{
			foreach (Recipe recipe in db.m_recipes)
			{
				if ((Object)(object)recipe?.m_craftingStation != (Object)null && MatchesStation(recipe.m_craftingStation, candidates))
				{
					return recipe.m_craftingStation;
				}
			}
			GameObject itemPrefab = db.GetItemPrefab("Hammer");
			PieceTable val = ((itemPrefab == null) ? null : itemPrefab.GetComponent<ItemDrop>()?.m_itemData?.m_shared?.m_buildPieces);
			if (val?.m_pieces != null)
			{
				foreach (GameObject piece in val.m_pieces)
				{
					if (!((Object)(object)piece == (Object)null))
					{
						CraftingStation component = piece.GetComponent<CraftingStation>();
						if ((Object)(object)component != (Object)null && MatchesStation(component, candidates))
						{
							return component;
						}
					}
				}
			}
			CraftingStation[] array = Resources.FindObjectsOfTypeAll<CraftingStation>();
			foreach (CraftingStation val2 in array)
			{
				if ((Object)(object)val2 != (Object)null && MatchesStation(val2, candidates))
				{
					return val2;
				}
			}
			return null;
		}

		private static bool MatchesStation(CraftingStation station, string[] candidates)
		{
			foreach (string b in candidates)
			{
				if (string.Equals(((Object)station).name, b, StringComparison.OrdinalIgnoreCase) || string.Equals(station.m_name, b, StringComparison.OrdinalIgnoreCase))
				{
					return true;
				}
			}
			return false;
		}
	}
	[HarmonyPatch(typeof(ObjectDB))]
	internal static class ObjectDBPatches
	{
		[HarmonyPatch("Awake")]
		[HarmonyPostfix]
		private static void AwakePostfix(ObjectDB __instance)
		{
			BalancedIdolRefinementPlugin.ConfigureIdolsAndRecipes(__instance);
		}

		[HarmonyPatch("CopyOtherDB")]
		[HarmonyPostfix]
		private static void CopyOtherDBPostfix(ObjectDB __instance)
		{
			BalancedIdolRefinementPlugin.ConfigureIdolsAndRecipes(__instance);
		}
	}
	[HarmonyPatch(typeof(InventoryGui), "DoCrafting")]
	internal static class InventoryGuiDoCraftingPatch
	{
		internal sealed class RefinementSnapshot
		{
			public ItemData OriginalItem;

			public int OriginalQuality;

			public float OriginalDurability;

			public Vector2i OriginalGridPos;

			public int OriginalVariant;

			public long OriginalCrafterId;

			public string OriginalCrafterName;

			public Dictionary<string, string> OriginalCustomData;
		}

		[HarmonyPrefix]
		private static void Prefix(Player player, Recipe ___m_craftRecipe, ItemData ___m_craftUpgradeItem, out RefinementSnapshot __state)
		{
			//IL_00f2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f7: Unknown result type (might be due to invalid IL or missing references)
			__state = null;
			if ((Object)(object)player == (Object)null || (Object)(object)___m_craftRecipe == (Object)null || ___m_craftUpgradeItem == null)
			{
				return;
			}
			CraftingStation val = (((Object)(object)Player.m_localPlayer != (Object)null) ? Player.m_localPlayer.GetCurrentCraftingStation() : player.GetCurrentCraftingStation());
			if ((Object)(object)val == (Object)null || !val.m_upgrader)
			{
				return;
			}
			if (___m_craftRecipe.m_resources != null)
			{
				Requirement[] resources = ___m_craftRecipe.m_resources;
				foreach (Requirement val2 in resources)
				{
					if (val2 != null && val2.m_upgraderResource && val2.m_resItem?.m_itemData?.m_shared != null)
					{
						val2.m_resItem.m_itemData.m_shared.m_breakChance = 0f;
						val2.m_resItem.m_itemData.m_shared.m_breakReturnIngreientsAmount = 0f;
					}
				}
			}
			__state = new RefinementSnapshot
			{
				OriginalItem = ___m_craftUpgradeItem,
				OriginalQuality = ___m_craftUpgradeItem.m_quality,
				OriginalDurability = ___m_craftUpgradeItem.m_durability,
				OriginalGridPos = ___m_craftUpgradeItem.m_gridPos,
				OriginalVariant = ___m_craftUpgradeItem.m_variant,
				OriginalCrafterId = ___m_craftUpgradeItem.m_crafterID,
				OriginalCrafterName = ___m_craftUpgradeItem.m_crafterName,
				OriginalCustomData = ((___m_craftUpgradeItem.m_customData != null) ? new Dictionary<string, string>(___m_craftUpgradeItem.m_customData) : null)
			};
		}

		[HarmonyTranspiler]
		private static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instructions)
		{
			//IL_005b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0065: Expected O, but got Unknown
			//IL_0078: Unknown result type (might be due to invalid IL or missing references)
			//IL_0082: Expected O, but got Unknown
			//IL_00ae: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b8: Expected O, but got Unknown
			//IL_00cb: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d5: Expected O, but got Unknown
			List<CodeInstruction> list = new List<CodeInstruction>(instructions);
			FieldInfo fieldInfo = AccessTools.Field(typeof(SharedData), "m_breakChance");
			FieldInfo fieldInfo2 = AccessTools.Field(typeof(SharedData), "m_breakReturnIngreientsAmount");
			for (int i = 0; i < list.Count; i++)
			{
				if (fieldInfo != null && CodeInstructionExtensions.LoadsField(list[i], fieldInfo, false))
				{
					list.Insert(i + 1, new CodeInstruction(OpCodes.Pop, (object)null));
					list.Insert(i + 2, new CodeInstruction(OpCodes.Ldc_R4, (object)(-1f)));
					i += 2;
				}
				else if (fieldInfo2 != null && CodeInstructionExtensions.LoadsField(list[i], fieldInfo2, false))
				{
					list.Insert(i + 1, new CodeInstruction(OpCodes.Pop, (object)null));
					list.Insert(i + 2, new CodeInstruction(OpCodes.Ldc_R4, (object)0f));
					i += 2;
				}
				else if (list[i].opcode == OpCodes.Ldstr && object.Equals(list[i].operand, "$msg_upgrader_failed"))
				{
					list[i].operand = "$msg_upgrader_fail";
				}
			}
			return list;
		}

		[HarmonyPostfix]
		private static void Postfix(InventoryGui __instance, Player player, RefinementSnapshot __state)
		{
			//IL_00d9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00de: Unknown result type (might be due to invalid IL or missing references)
			if (__state == null || (Object)(object)player == (Object)null)
			{
				return;
			}
			Inventory inventory = ((Humanoid)player).GetInventory();
			if (inventory == null)
			{
				return;
			}
			int num = Math.Max(1, __state.OriginalQuality - 1);
			ItemData val = inventory.GetItemAt(__state.OriginalGridPos.x, __state.OriginalGridPos.y);
			if (val == null && __state.OriginalItem?.m_shared != null)
			{
				val = inventory.GetItem(__state.OriginalItem.m_shared.m_name, num, false) ?? inventory.GetItem(__state.OriginalItem.m_shared.m_name, __state.OriginalQuality + 1, false);
			}
			if (val == null && __state.OriginalItem != null)
			{
				__state.OriginalItem.m_quality = num;
				__state.OriginalItem.m_durability = Mathf.Min(__state.OriginalDurability, __state.OriginalItem.GetMaxDurability());
				__state.OriginalItem.m_gridPos = __state.OriginalGridPos;
				inventory.AddItem(__state.OriginalItem);
				AccessTools.Method(typeof(InventoryGui), "UpdateCraftingPanel", (Type[])null, (Type[])null)?.Invoke(__instance, new object[1] { false });
			}
			else if (val != null && val.m_quality <= __state.OriginalQuality)
			{
				val.m_quality = num;
				val.m_durability = Mathf.Min(__state.OriginalDurability, val.GetMaxDurability());
				val.m_variant = __state.OriginalVariant;
				val.m_crafterID = __state.OriginalCrafterId;
				val.m_crafterName = __state.OriginalCrafterName;
				if (__state.OriginalCustomData != null)
				{
					val.m_customData = new Dictionary<string, string>(__state.OriginalCustomData);
				}
			}
		}
	}
}