Decompiled source of Stake Tweaks v1.0.0

BepInEx/plugins/StakeTweaks/StakeTweaks.dll

Decompiled a week ago
using System;
using System.Collections;
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 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("StakeTweaks")]
[assembly: AssemblyConfiguration("Debug")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0")]
[assembly: AssemblyProduct("StakeTweaks")]
[assembly: AssemblyTitle("StakeTweaks")]
[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 StakeTune
{
	[BepInPlugin("com.toolkings.staketune", "Stake Tweaks", "1.0.0")]
	public sealed class StakeTunePlugin : BaseUnityPlugin
	{
		private sealed class TargetDefinition
		{
			internal readonly string PrefabName;

			internal readonly string NameEn;

			internal readonly string NameEs;

			internal readonly string Section;

			internal readonly bool HasDamage;

			internal TargetDefinition(string prefabName, string nameEn, string nameEs, string section, bool hasDamage)
			{
				PrefabName = prefabName;
				NameEn = nameEn;
				NameEs = nameEs;
				Section = section;
				HasDamage = hasDamage;
			}
		}

		private sealed class TargetSettings
		{
			internal readonly TargetDefinition Definition;

			internal ConfigEntry<float>? DamageMultiplier;

			internal ConfigEntry<float>? HealthMultiplier;

			internal ConfigEntry<float>? DamageTakenMultiplier;

			internal ConfigEntry<bool>? RefundMaterials;

			internal readonly Dictionary<string, ConfigEntry<int>> RecipeAmounts = new Dictionary<string, ConfigEntry<int>>(StringComparer.OrdinalIgnoreCase);

			internal TargetSettings(TargetDefinition definition)
			{
				Definition = definition;
			}
		}

		[HarmonyPatch(typeof(ObjectDB), "Awake")]
		private static class ObjectDbAwakePatch
		{
			[HarmonyPostfix]
			private static void Postfix(ObjectDB __instance)
			{
				_instance?.ConfigureBuildPieces(__instance);
			}
		}

		[HarmonyPatch(typeof(WearNTear), "Awake")]
		private static class WearNTearAwakePatch
		{
			[HarmonyPrefix]
			private static void Prefix(WearNTear __instance)
			{
				TargetSettings targetSettings = _instance?.FindSettings(GetPrefabName((Component)(object)__instance));
				if (targetSettings?.HealthMultiplier != null)
				{
					__instance.m_health *= Math.Max(0.1f, targetSettings.HealthMultiplier.Value);
				}
			}
		}

		[HarmonyPatch(typeof(WearNTear), "RPC_Damage")]
		private static class WearNTearDamagePatch
		{
			[HarmonyPrefix]
			private static void Prefix(WearNTear __instance, HitData hit)
			{
				//IL_003a: Unknown result type (might be due to invalid IL or missing references)
				//IL_0054: Unknown result type (might be due to invalid IL or missing references)
				//IL_0059: Unknown result type (might be due to invalid IL or missing references)
				if (hit != null)
				{
					TargetSettings targetSettings = _instance?.FindSettings(GetPrefabName((Component)(object)__instance));
					if (targetSettings?.DamageTakenMultiplier != null)
					{
						hit.m_damage = MultiplyDamage(hit.m_damage, Math.Max(0f, targetSettings.DamageTakenMultiplier.Value));
					}
				}
			}
		}

		[HarmonyPatch(typeof(Aoe), "GetDamage", new Type[] { typeof(int) })]
		private static class StakeAoeDamagePatch
		{
			[HarmonyPostfix]
			private static void Postfix(Aoe __instance, ref DamageTypes __result)
			{
				//IL_0038: Unknown result type (might be due to invalid IL or missing references)
				//IL_0052: Unknown result type (might be due to invalid IL or missing references)
				//IL_0057: Unknown result type (might be due to invalid IL or missing references)
				StakeTunePlugin instance = _instance;
				if (!((Object)(object)instance == (Object)null))
				{
					TargetSettings targetSettings = instance.FindSettings(GetPrefabName((Component)(object)__instance));
					if (targetSettings?.DamageMultiplier != null)
					{
						__result = MultiplyDamage(__result, Math.Max(0f, targetSettings.DamageMultiplier.Value));
					}
				}
			}
		}

		public const string PluginGuid = "com.toolkings.staketune";

		public const string PluginName = "Stake Tweaks";

		public const string PluginVersion = "1.0.0";

		private static readonly TargetDefinition[] Definitions = new TargetDefinition[5]
		{
			new TargetDefinition("piece_sharpstakes", "Sharp stakes", "Estacas afiladas", "STAKES/Sharp Stakes", hasDamage: true),
			new TargetDefinition("piece_dvergr_sharpstakes", "Dvergr sharp stakes", "Estacas afiladas dvergr", "STAKES/Dvergr Sharp Stakes", hasDamage: true),
			new TargetDefinition("piece_stakewall_blackwood", "Ashwood stakewall", "Muro de estacas de madera de fresno", "STAKES/Ashwood Stakewall", hasDamage: true),
			new TargetDefinition("stake_wall", "Stakewall", "Muro de estacas", "STAKEWALLS/Stakewall", hasDamage: false),
			new TargetDefinition("piece_dvergr_stake_wall", "Dvergr stakewall", "Muro de estacas dvergr", "STAKEWALLS/Dvergr Stakewall", hasDamage: false)
		};

		private static readonly Dictionary<string, TargetDefinition> DefinitionByPrefab = Definitions.ToDictionary<TargetDefinition, string>((TargetDefinition definition) => definition.PrefabName, StringComparer.OrdinalIgnoreCase);

		private static StakeTunePlugin? _instance;

		private readonly Dictionary<string, TargetSettings> _settings = new Dictionary<string, TargetSettings>(StringComparer.OrdinalIgnoreCase);

		private readonly Dictionary<string, Requirement[]> _originalRequirements = new Dictionary<string, Requirement[]>(StringComparer.OrdinalIgnoreCase);

		private readonly HashSet<string> _configuredPieces = new HashSet<string>(StringComparer.OrdinalIgnoreCase);

		private Coroutine? _scanRoutine;

		private bool _isSpanish;

		private bool _settingsBound;

		private bool _configNeedsSave;

		private int _displayOrder;

		private void Awake()
		{
			//IL_001a: Unknown result type (might be due to invalid IL or missing references)
			_instance = this;
			DetectLanguage();
			BindTargetSettings();
			new Harmony("com.toolkings.staketune").PatchAll(typeof(StakeTunePlugin).Assembly);
			_scanRoutine = ((MonoBehaviour)this).StartCoroutine(WaitForBuildPieces());
		}

		private IEnumerator WaitForBuildPieces()
		{
			yield return (object)new WaitForSeconds(1f);
			for (int attempt = 0; attempt < 12; attempt++)
			{
				if ((Object)(object)ObjectDB.instance != (Object)null)
				{
					ConfigureBuildPieces(ObjectDB.instance);
					SavePendingConfig();
					_scanRoutine = null;
					yield break;
				}
				yield return (object)new WaitForSeconds(1f);
			}
			((BaseUnityPlugin)this).Logger.LogWarning((object)Text("Valheim's build-piece list was not ready. StakeTune will try again when ObjectDB loads.", "La lista de piezas de Valheim no estaba lista. StakeTune lo intentará de nuevo cuando cargue ObjectDB."));
			_scanRoutine = null;
		}

		private void DetectLanguage()
		{
			string text = null;
			try
			{
				Localization instance = Localization.instance;
				text = ((instance != null) ? instance.GetSelectedLanguage() : null);
			}
			catch
			{
			}
			if (!string.IsNullOrWhiteSpace(text))
			{
				_isSpanish = text.StartsWith("Spanish", StringComparison.OrdinalIgnoreCase) || text.StartsWith("Español", StringComparison.OrdinalIgnoreCase) || text.StartsWith("es", StringComparison.OrdinalIgnoreCase);
			}
			else
			{
				_isSpanish = CultureInfo.CurrentUICulture.TwoLetterISOLanguageName.Equals("es", StringComparison.OrdinalIgnoreCase) || CultureInfo.CurrentCulture.TwoLetterISOLanguageName.Equals("es", StringComparison.OrdinalIgnoreCase);
			}
		}

		private void BindTargetSettings()
		{
			if (_settingsBound)
			{
				return;
			}
			TargetDefinition[] definitions = Definitions;
			foreach (TargetDefinition targetDefinition in definitions)
			{
				string targetName = GetTargetName(targetDefinition);
				TargetSettings targetSettings = new TargetSettings(targetDefinition);
				if (targetDefinition.HasDamage)
				{
					targetSettings.DamageMultiplier = ((BaseUnityPlugin)this).Config.Bind<float>(targetDefinition.Section, targetDefinition.PrefabName + ".DamageMultiplier", 2.5f, CreateFloatDescription(Text("Damage dealt multiplier. 2.5 means 2.5 times the original damage.", "Multiplicador del daño infligido. 2.5 significa 2.5 veces el daño original."), targetName + Text(" — damage multiplier", " — multiplicador de daño"), 0f, 100f));
				}
				targetSettings.HealthMultiplier = ((BaseUnityPlugin)this).Config.Bind<float>(targetDefinition.Section, targetDefinition.PrefabName + ".HealthMultiplier", 3f, CreateFloatDescription(Text("Maximum structure health multiplier. 3 means three times the original health.", "Multiplicador de salud máxima. 3 significa tres veces la salud original."), targetName + Text(" — health multiplier", " — multiplicador de salud"), 0.1f, 100f));
				targetSettings.DamageTakenMultiplier = ((BaseUnityPlugin)this).Config.Bind<float>(targetDefinition.Section, targetDefinition.PrefabName + ".DamageTakenMultiplier", 1f, CreateFloatDescription(Text("Damage received multiplier: 1 means 100% of incoming damage; 0.1 means 10%.", "Multiplicador del daño recibido: 1 significa 100% del daño; 0.1 significa 10%."), targetName + Text(" — damage received (1 = 100%)", " — daño recibido (1 = 100%)"), 0f, 10f));
				targetSettings.RefundMaterials = ((BaseUnityPlugin)this).Config.Bind<bool>(targetDefinition.Section, targetDefinition.PrefabName + ".RefundMaterials", true, CreateDescription(Text("Return all original crafting materials when this player-built piece is destroyed.", "Devuelve todos los materiales originales cuando se destruye esta pieza construida por un jugador."), targetName + Text(" — return 100% of materials", " — devolver 100% de materiales")));
				_settings.Add(targetDefinition.PrefabName, targetSettings);
			}
			_settingsBound = true;
		}

		private void ConfigureBuildPieces(ObjectDB database)
		{
			List<Piece> allBuildPieces;
			try
			{
				allBuildPieces = database.GetAllBuildPieces(true);
			}
			catch (Exception ex)
			{
				((BaseUnityPlugin)this).Logger.LogWarning((object)(Text("Could not read the building catalog: ", "No se pudo leer el catálogo de construcción: ") + ex.Message));
				return;
			}
			if (allBuildPieces == null)
			{
				return;
			}
			TargetDefinition[] definitions = Definitions;
			foreach (TargetDefinition definition in definitions)
			{
				Piece val = ((IEnumerable<Piece>)allBuildPieces).FirstOrDefault((Func<Piece, bool>)((Piece candidate) => (Object)(object)candidate != (Object)null && (Object)(object)((Component)candidate).gameObject != (Object)null && NormalizePrefabName(((Object)((Component)candidate).gameObject).name).Equals(definition.PrefabName, StringComparison.OrdinalIgnoreCase)));
				if ((Object)(object)val == (Object)null)
				{
					((BaseUnityPlugin)this).Logger.LogWarning((object)(Text("Could not find build piece ", "No se encontró la pieza de construcción ") + definition.PrefabName));
				}
				else
				{
					ConfigurePiece(val, definition);
				}
			}
			if (_configNeedsSave)
			{
				((BaseUnityPlugin)this).Config.Save();
				_configNeedsSave = false;
			}
		}

		private void ConfigurePiece(Piece piece, TargetDefinition definition)
		{
			if (_configuredPieces.Add(definition.PrefabName))
			{
				TargetSettings settings = _settings[definition.PrefabName];
				Requirement[] array = CloneRequirements(piece.m_resources ?? Array.Empty<Requirement>());
				_originalRequirements[definition.PrefabName] = array;
				BindRecipeAmounts(piece, definition, settings, array);
				piece.m_resources = ApplyRecipeAndRecovery(array, settings);
				((BaseUnityPlugin)this).Logger.LogInfo((object)(Text("Configured ", "Configurado: ") + GetTargetName(definition) + " (" + definition.PrefabName + ")"));
			}
		}

		private void BindRecipeAmounts(Piece piece, TargetDefinition definition, TargetSettings settings, Requirement[] original)
		{
			//IL_0155: Unknown result type (might be due to invalid IL or missing references)
			//IL_015f: Expected O, but got Unknown
			foreach (IGrouping<string, Requirement> item in original.Where((Requirement requirement) => (Object)(object)requirement?.m_resItem != (Object)null && (Object)(object)((Component)requirement.m_resItem).gameObject != (Object)null).GroupBy<Requirement, string>((Requirement requirement) => ((Object)((Component)requirement.m_resItem).gameObject).name, StringComparer.OrdinalIgnoreCase))
			{
				string key = item.Key;
				int num = Math.Max(0, item.Sum((Requirement requirement) => requirement.m_amount));
				string localizedPieceName = GetLocalizedPieceName(piece, definition);
				string localizedResourceName = GetLocalizedResourceName(item.First().m_resItem, key);
				string displayName = localizedPieceName + " (" + definition.PrefabName + ") — " + localizedResourceName;
				string text = Text("Exact amount of " + localizedResourceName + " required to craft this piece. The default is the piece's original recipe; set 0 for free. Range: 0 to the largest integer supported by the game.", "Cantidad exacta de " + localizedResourceName + " necesaria para fabricar esta pieza. El valor inicial es su receta original; usa 0 para que sea gratis. Rango: 0 hasta el entero máximo que admite el juego.");
				string text2 = definition.PrefabName + ".Material." + key;
				settings.RecipeAmounts[key] = ((BaseUnityPlugin)this).Config.Bind<int>(definition.Section, text2, num, new ConfigDescription(text, (AcceptableValueBase)(object)new AcceptableValueRange<int>(0, int.MaxValue), new object[1] { CreateAttributes(displayName) }));
				_configNeedsSave = true;
			}
		}

		private static Requirement[] ApplyRecipeAndRecovery(Requirement[] original, TargetSettings settings)
		{
			List<Requirement> list = new List<Requirement>(original.Length);
			HashSet<string> hashSet = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
			foreach (Requirement val in original)
			{
				if (val == null)
				{
					continue;
				}
				Requirement val2 = CloneRequirement(val);
				val2.m_recover = settings.RefundMaterials?.Value ?? val?.m_recover ?? false;
				if ((Object)(object)val2.m_resItem != (Object)null && (Object)(object)((Component)val2.m_resItem).gameObject != (Object)null)
				{
					string name = ((Object)((Component)val2.m_resItem).gameObject).name;
					if (settings.RecipeAmounts.TryGetValue(name, out ConfigEntry<int> value))
					{
						if (!hashSet.Add(name))
						{
							continue;
						}
						val2.m_amount = Math.Max(0, value.Value);
						val2.m_extraAmountOnlyOneIngredient = 0;
						val2.m_amountPerLevel = 0;
					}
				}
				list.Add(val2);
			}
			return list.ToArray();
		}

		private static Requirement[] CloneRequirements(Requirement[] original)
		{
			return original.Select(CloneRequirement).ToArray();
		}

		private static Requirement CloneRequirement(Requirement original)
		{
			//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_0012: Unknown result type (might be due to invalid IL or missing references)
			//IL_001e: 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_0036: Unknown result type (might be due to invalid IL or missing references)
			//IL_0042: Unknown result type (might be due to invalid IL or missing references)
			//IL_004f: Expected O, but got Unknown
			return new Requirement
			{
				m_resItem = original.m_resItem,
				m_amount = original.m_amount,
				m_extraAmountOnlyOneIngredient = original.m_extraAmountOnlyOneIngredient,
				m_amountPerLevel = original.m_amountPerLevel,
				m_upgraderResource = original.m_upgraderResource,
				m_recover = original.m_recover
			};
		}

		private ConfigDescription CreateFloatDescription(string description, string displayName, float minimum, float maximum)
		{
			//IL_001a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0020: Expected O, but got Unknown
			return new ConfigDescription(description, (AcceptableValueBase)(object)new AcceptableValueRange<float>(minimum, maximum), new object[1] { CreateAttributes(displayName) });
		}

		private ConfigDescription CreateDescription(string description, string displayName)
		{
			//IL_0013: Unknown result type (might be due to invalid IL or missing references)
			//IL_0019: Expected O, but got Unknown
			return new ConfigDescription(description, (AcceptableValueBase)null, new object[1] { CreateAttributes(displayName) });
		}

		private ConfigurationManagerAttributes CreateAttributes(string displayName)
		{
			return new ConfigurationManagerAttributes
			{
				DispName = displayName,
				Order = _displayOrder++
			};
		}

		private TargetSettings? FindSettings(string prefabName)
		{
			string key = NormalizePrefabName(prefabName);
			TargetSettings value;
			return _settings.TryGetValue(key, out value) ? value : null;
		}

		private static string NormalizePrefabName(string name)
		{
			return name.EndsWith("(Clone)", StringComparison.Ordinal) ? name.Substring(0, name.Length - "(Clone)".Length).TrimEnd() : name;
		}

		private static string GetPrefabName(Component component)
		{
			Piece componentInParent = component.GetComponentInParent<Piece>();
			return NormalizePrefabName(((Object)(object)componentInParent != (Object)null) ? ((Object)((Component)componentInParent).gameObject).name : ((Object)component.gameObject).name);
		}

		private string GetTargetName(TargetDefinition definition)
		{
			return _isSpanish ? definition.NameEs : definition.NameEn;
		}

		private string GetLocalizedPieceName(Piece piece, TargetDefinition definition)
		{
			if (!string.IsNullOrWhiteSpace(piece.m_name))
			{
				try
				{
					Localization instance = Localization.instance;
					if (instance != null)
					{
						string text = instance.Localize(piece.m_name);
						if (!string.IsNullOrWhiteSpace(text))
						{
							return text;
						}
					}
				}
				catch
				{
				}
			}
			return GetTargetName(definition);
		}

		private static string GetLocalizedResourceName(ItemDrop item, string fallback)
		{
			try
			{
				Localization instance = Localization.instance;
				string text = item.m_itemData?.m_shared?.m_name;
				if (instance != null && !string.IsNullOrWhiteSpace(text))
				{
					string text2 = instance.Localize(text);
					if (!string.IsNullOrWhiteSpace(text2))
					{
						return text2;
					}
				}
			}
			catch
			{
			}
			return fallback;
		}

		private string Text(string english, string spanish)
		{
			return _isSpanish ? spanish : english;
		}

		private void SavePendingConfig()
		{
			if (_configNeedsSave)
			{
				((BaseUnityPlugin)this).Config.Save();
				_configNeedsSave = false;
			}
		}

		private static DamageTypes MultiplyDamage(DamageTypes damage, float multiplier)
		{
			//IL_0091: Unknown result type (might be due to invalid IL or missing references)
			//IL_0092: Unknown result type (might be due to invalid IL or missing references)
			//IL_0095: Unknown result type (might be due to invalid IL or missing references)
			damage.m_damage *= multiplier;
			damage.m_blunt *= multiplier;
			damage.m_slash *= multiplier;
			damage.m_pierce *= multiplier;
			damage.m_chop *= multiplier;
			damage.m_pickaxe *= multiplier;
			damage.m_fire *= multiplier;
			damage.m_frost *= multiplier;
			damage.m_lightning *= multiplier;
			damage.m_poison *= multiplier;
			damage.m_spirit *= multiplier;
			damage.m_nonPlayer *= multiplier;
			return damage;
		}
	}
	internal sealed class ConfigurationManagerAttributes
	{
		public string? DispName;

		public int? Order;
	}
}