Decompiled source of LithoCost v1.0.0

BepInEx/plugins/LithoCost/LithoCost.dll

Decompiled a week 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 BepInEx;
using BepInEx.Configuration;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using UnityEngine;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)]
[assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")]
[assembly: AssemblyCompany("LithoCost")]
[assembly: AssemblyConfiguration("Debug")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0")]
[assembly: AssemblyProduct("LithoCost")]
[assembly: AssemblyTitle("LithoCost")]
[assembly: AssemblyVersion("1.0.0.0")]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)]
	internal sealed class NullableAttribute : Attribute
	{
		public readonly byte[] NullableFlags;

		public NullableAttribute(byte P_0)
		{
			NullableFlags = new byte[1] { P_0 };
		}

		public NullableAttribute(byte[] P_0)
		{
			NullableFlags = P_0;
		}
	}
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)]
	internal sealed class NullableContextAttribute : Attribute
	{
		public readonly byte Flag;

		public NullableContextAttribute(byte P_0)
		{
			Flag = P_0;
		}
	}
	[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 LithoCost
{
	[BepInPlugin("com.lithocost.valheim", "LithoCost", "1.0.0")]
	public sealed class LithoCostPlugin : BaseUnityPlugin
	{
		private sealed class CostRule
		{
			internal readonly ConfigEntry<bool> Enabled;

			internal readonly ConfigEntry<string> ReplacementPrefab;

			internal readonly ConfigEntry<int> Amount;

			internal CostRule(ConfigEntry<bool> enabled, ConfigEntry<string> replacementPrefab, ConfigEntry<int> amount)
			{
				Enabled = enabled;
				ReplacementPrefab = replacementPrefab;
				Amount = amount;
			}
		}

		[HarmonyPatch(typeof(ObjectDB), "Awake")]
		private static class ObjectDbAwakePatch
		{
			[HarmonyPostfix]
			private static void Postfix()
			{
				RequestScan();
			}
		}

		[HarmonyPatch(typeof(ZNetScene), "Awake")]
		private static class ZNetSceneAwakePatch
		{
			[HarmonyPostfix]
			private static void Postfix()
			{
				RequestScan();
			}
		}

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

		public const string PluginName = "LithoCost";

		public const string PluginVersion = "1.0.0";

		private static LithoCostPlugin? _instance;

		private readonly Dictionary<Piece, Requirement[]> _originalRequirements = new Dictionary<Piece, Requirement[]>();

		private readonly Dictionary<string, CostRule> _materialRules = new Dictionary<string, CostRule>(StringComparer.OrdinalIgnoreCase);

		private readonly Dictionary<string, CostRule> _pieceRules = new Dictionary<string, CostRule>(StringComparer.OrdinalIgnoreCase);

		private ConfigEntry<string>? _resourceKeywords;

		private Coroutine? _scanRoutine;

		private bool _hasPendingConfigSave;

		private void Awake()
		{
			//IL_0031: Unknown result type (might be due to invalid IL or missing references)
			//IL_0037: Expected O, but got Unknown
			_instance = this;
			_resourceKeywords = ((BaseUnityPlugin)this).Config.Bind<string>("General", "RockResourceKeywords", "stone,marble,grausten,rock,crystal,obsidian", "Comma-separated words used to identify rocky construction materials by their prefab name. Add terms here for modded rock or crystal resources.");
			Harmony val = new Harmony("com.lithocost.valheim");
			val.PatchAll(typeof(LithoCostPlugin).Assembly);
			((BaseUnityPlugin)this).Logger.LogInfo((object)"LithoCost loaded. Rock building costs are unchanged until an override is enabled in the config.");
		}

		internal static void RequestScan()
		{
			if (!((Object)(object)_instance == (Object)null) && _instance._scanRoutine == null)
			{
				_instance._scanRoutine = ((MonoBehaviour)_instance).StartCoroutine(_instance.ScanRoutine());
			}
		}

		private IEnumerator ScanRoutine()
		{
			yield return (object)new WaitForSeconds(2f);
			for (int attempt = 0; attempt < 8; attempt++)
			{
				DiscoverNewPieces();
				yield return (object)new WaitForSeconds(1f);
			}
			_scanRoutine = null;
		}

		private void DiscoverNewPieces()
		{
			ObjectDB instance = ObjectDB.instance;
			if ((Object)(object)instance == (Object)null)
			{
				return;
			}
			List<Piece> allBuildPieces;
			try
			{
				allBuildPieces = instance.GetAllBuildPieces(true);
			}
			catch (Exception ex)
			{
				((BaseUnityPlugin)this).Logger.LogWarning((object)("Could not read Valheim's build-piece list yet: " + ex.Message));
				return;
			}
			if (allBuildPieces == null)
			{
				return;
			}
			int num = 0;
			foreach (Piece item in allBuildPieces)
			{
				if ((Object)(object)item == (Object)null || (Object)(object)((Component)item).gameObject == (Object)null || item.m_resources == null)
				{
					continue;
				}
				string name = ((Object)((Component)item).gameObject).name;
				if (_pieceRules.ContainsKey(name))
				{
					continue;
				}
				Requirement[] array = (Requirement[])item.m_resources.Clone();
				List<Requirement> list = array.Where((Requirement requirement) => IsRockResource(GetResourcePrefabName(requirement))).ToList();
				if (list.Count == 0)
				{
					continue;
				}
				_originalRequirements[item] = array;
				foreach (string item2 in (from value in list.Select(GetResourcePrefabName)
					where !string.IsNullOrWhiteSpace(value)
					select value).Distinct<string>(StringComparer.OrdinalIgnoreCase))
				{
					EnsureMaterialRule(item2);
				}
				EnsurePieceRule(item, name);
				num++;
				ApplyRules(item, array);
			}
			if (_hasPendingConfigSave)
			{
				((BaseUnityPlugin)this).Config.Save();
				_hasPendingConfigSave = false;
			}
			if (num > 0)
			{
				((BaseUnityPlugin)this).Logger.LogInfo((object)("Discovered " + num + " rock-based build piece(s). Enable overrides in BepInEx/config/com.lithocost.valheim.cfg."));
			}
		}

		private CostRule EnsureMaterialRule(string resourcePrefab)
		{
			if (_materialRules.TryGetValue(resourcePrefab, out CostRule value))
			{
				return value;
			}
			string text = "Material: " + resourcePrefab;
			CostRule costRule = new CostRule(((BaseUnityPlugin)this).Config.Bind<bool>(text, "Enabled", false, "Apply this cost to every detected build piece that uses " + resourcePrefab + "."), ((BaseUnityPlugin)this).Config.Bind<string>(text, "ReplacementPrefab", "Stone", "Item prefab ID to charge instead of " + resourcePrefab + ". Example: Stone."), ((BaseUnityPlugin)this).Config.Bind<int>(text, "Amount", 1, "Replacement quantity per build piece. Set to 0 to remove this material cost."));
			_materialRules.Add(resourcePrefab, costRule);
			_hasPendingConfigSave = true;
			return costRule;
		}

		private CostRule EnsurePieceRule(Piece piece, string prefabName)
		{
			if (_pieceRules.TryGetValue(prefabName, out CostRule value))
			{
				return value;
			}
			string text = (string.IsNullOrWhiteSpace(piece.m_name) ? prefabName : piece.m_name);
			string text2 = "Piece: " + prefabName;
			CostRule costRule = new CostRule(((BaseUnityPlugin)this).Config.Bind<bool>(text2, "Enabled", false, "Override all rocky ingredients for " + text + " (prefab: " + prefabName + "). This takes priority over material rules."), ((BaseUnityPlugin)this).Config.Bind<string>(text2, "ReplacementPrefab", "Stone", "Item prefab ID charged for this piece. Example: Stone, BlackMarble, or Crystal."), ((BaseUnityPlugin)this).Config.Bind<int>(text2, "Amount", 1, "Replacement quantity for this piece. Set to 0 to remove all rocky material costs."));
			_pieceRules.Add(prefabName, costRule);
			_hasPendingConfigSave = true;
			return costRule;
		}

		private void ApplyRules(Piece piece, Requirement[] original)
		{
			if ((Object)(object)piece == (Object)null || (Object)(object)ObjectDB.instance == (Object)null)
			{
				return;
			}
			string name = ((Object)((Component)piece).gameObject).name;
			if (!_pieceRules.TryGetValue(name, out CostRule value))
			{
				piece.m_resources = original;
				return;
			}
			if (value.Enabled.Value)
			{
				Requirement val = ((IEnumerable<Requirement>)original).FirstOrDefault((Func<Requirement, bool>)((Requirement requirement) => IsRockResource(GetResourcePrefabName(requirement))));
				if (val == null)
				{
					piece.m_resources = original;
					return;
				}
				ItemDrop val2 = FindItemDrop(value.ReplacementPrefab.Value);
				if ((Object)(object)val2 == (Object)null)
				{
					WarnUnknownItem(value.ReplacementPrefab.Value, name);
					piece.m_resources = original;
					return;
				}
				List<Requirement> list = original.Where((Requirement requirement) => !IsRockResource(GetResourcePrefabName(requirement))).ToList();
				if (value.Amount.Value > 0)
				{
					list.Add(CreateReplacement(val, val2, value.Amount.Value));
				}
				piece.m_resources = list.ToArray();
				return;
			}
			List<Requirement> list2 = new List<Requirement>(original.Length);
			foreach (Requirement val3 in original)
			{
				string resourcePrefabName = GetResourcePrefabName(val3);
				if (!IsRockResource(resourcePrefabName) || resourcePrefabName == null || !_materialRules.TryGetValue(resourcePrefabName, out CostRule value2) || !value2.Enabled.Value)
				{
					list2.Add(val3);
					continue;
				}
				ItemDrop val4 = FindItemDrop(value2.ReplacementPrefab.Value);
				if ((Object)(object)val4 == (Object)null)
				{
					WarnUnknownItem(value2.ReplacementPrefab.Value, name);
					list2.Add(val3);
				}
				else if (value2.Amount.Value > 0)
				{
					list2.Add(CreateReplacement(val3, val4, value2.Amount.Value));
				}
			}
			piece.m_resources = list2.ToArray();
		}

		private ItemDrop? FindItemDrop(string prefabName)
		{
			if (string.IsNullOrWhiteSpace(prefabName) || (Object)(object)ObjectDB.instance == (Object)null)
			{
				return null;
			}
			GameObject itemPrefab = ObjectDB.instance.GetItemPrefab(prefabName.Trim());
			return ((Object)(object)itemPrefab == (Object)null) ? null : itemPrefab.GetComponent<ItemDrop>();
		}

		private static Requirement CreateReplacement(Requirement template, ItemDrop item, int amount)
		{
			//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_000d: 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_001b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0022: Unknown result type (might be due to invalid IL or missing references)
			//IL_002e: Unknown result type (might be due to invalid IL or missing references)
			//IL_003b: Expected O, but got Unknown
			return new Requirement
			{
				m_resItem = item,
				m_amount = amount,
				m_extraAmountOnlyOneIngredient = 0,
				m_amountPerLevel = 0,
				m_upgraderResource = template.m_upgraderResource,
				m_recover = template.m_recover
			};
		}

		private void WarnUnknownItem(string itemPrefab, string piecePrefab)
		{
			((BaseUnityPlugin)this).Logger.LogWarning((object)("Item prefab '" + itemPrefab + "' was not found for build piece '" + piecePrefab + "'; its original cost was kept."));
		}

		private bool IsRockResource(string? resourcePrefab)
		{
			if (string.IsNullOrWhiteSpace(resourcePrefab) || _resourceKeywords == null)
			{
				return false;
			}
			string[] source = _resourceKeywords.Value.Split(new char[2] { ',', ';' }, StringSplitOptions.RemoveEmptyEntries);
			return source.Any((string keyword) => !string.IsNullOrWhiteSpace(keyword) && resourcePrefab.IndexOf(keyword.Trim(), StringComparison.OrdinalIgnoreCase) >= 0);
		}

		private static string? GetResourcePrefabName(Requirement? requirement)
		{
			if (requirement == null || (Object)(object)requirement.m_resItem == (Object)null || (Object)(object)((Component)requirement.m_resItem).gameObject == (Object)null)
			{
				return null;
			}
			return ((Object)((Component)requirement.m_resItem).gameObject).name;
		}
	}
}