Decompiled source of ArmorMouldDropRates v1.0.0

BepInEx/plugins/ArmorMouldDropRates/ArmorMouldDropRates.dll

Decompiled 2 days ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
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.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")]
[assembly: AssemblyCompany("ArmorMouldDropRates")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0")]
[assembly: AssemblyProduct("ArmorMouldDropRates")]
[assembly: AssemblyTitle("ArmorMouldDropRates")]
[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.Module, AllowMultiple = false, Inherited = false)]
	internal sealed class RefSafetyRulesAttribute : Attribute
	{
		public readonly int Version;

		public RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
}
namespace ArmorMouldDropRates
{
	public enum DropRateMode
	{
		Fixed,
		Multiplier
	}
	internal sealed class ChanceSettings
	{
		internal readonly DropRateMode Mode;

		internal readonly float FixedChance;

		internal readonly float Multiplier;

		internal ChanceSettings(string mode, float fixedChance, float multiplier, Action<string> warn)
		{
			if (string.Equals(mode?.Trim(), "Fixed", StringComparison.OrdinalIgnoreCase))
			{
				Mode = DropRateMode.Fixed;
			}
			else
			{
				Mode = DropRateMode.Multiplier;
				if (!string.Equals(mode?.Trim(), "Multiplier", StringComparison.OrdinalIgnoreCase))
				{
					warn("Invalid Mode; falling back to Multiplier.");
				}
			}
			FixedChance = Validate(fixedChance, 0.1f, 1f, "FixedChance", warn);
			Multiplier = Validate(multiplier, 3f, float.MaxValue, "Multiplier", warn);
		}

		private static float Validate(float value, float fallback, float max, string key, Action<string> warn)
		{
			if (float.IsNaN(value) || float.IsInfinity(value))
			{
				warn(key + " must be finite; using " + fallback + ".");
				return fallback;
			}
			float num = Math.Max(0f, Math.Min(max, value));
			if (num != value)
			{
				warn(key + " is outside its allowed range; clamped to " + num + ".");
			}
			return num;
		}

		internal float Apply(float original)
		{
			if (Mode != DropRateMode.Fixed)
			{
				return (float)Math.Max(0.0, Math.Min(1.0, (double)original * (double)Multiplier));
			}
			return FixedChance;
		}
	}
	internal sealed class DropProcessor
	{
		private sealed class Baseline
		{
			internal readonly float Chance;

			internal Baseline(float chance)
			{
				Chance = chance;
			}
		}

		internal static readonly string[] KrigenMoulds = new string[6] { "MoldArmorGoldChest", "MoldArmorGoldHelmet", "MoldArmorGoldLegs", "MoldArmormediumChest", "MoldArmorMediumHelmet", "MoldArmorMediumLegs" };

		internal static readonly string[] HexenMoulds = new string[3] { "MoldArmorMageChest", "MoldArmorMageHelmet", "MoldArmorMageLegs" };

		private readonly ConditionalWeakTable<Drop, Baseline> originals = new ConditionalWeakTable<Drop, Baseline>();

		internal void Process(IEnumerable<GameObject> prefabs, ChanceSettings settings, Action<string> info, Action<string> warn, Action<string> debug)
		{
			int num = 0;
			int num2 = 0;
			HashSet<GameObject> hashSet = new HashSet<GameObject>();
			HashSet<string> hashSet2 = new HashSet<string>(StringComparer.Ordinal);
			string[] array2;
			foreach (GameObject prefab in prefabs)
			{
				if (!Object.op_Implicit((Object)(object)prefab) || !hashSet.Add(prefab))
				{
					continue;
				}
				string[] array;
				if (((Object)prefab).name == "JotunWarrior")
				{
					array = KrigenMoulds;
				}
				else
				{
					if (!(((Object)prefab).name == "JotunWitch"))
					{
						continue;
					}
					array = HexenMoulds;
				}
				hashSet2.Add(((Object)prefab).name);
				info(((Object)prefab).name + " found.");
				HashSet<string> hashSet3 = new HashSet<string>(array, StringComparer.Ordinal);
				HashSet<string> hashSet4 = new HashSet<string>(StringComparer.Ordinal);
				CharacterDrop component = prefab.GetComponent<CharacterDrop>();
				int num3 = 0;
				int num4 = 0;
				if (!Object.op_Implicit((Object)(object)component) || component.m_drops == null)
				{
					warn(((Object)prefab).name + " has no CharacterDrop table.");
				}
				else
				{
					foreach (Drop drop in component.m_drops)
					{
						if (drop == null || !Object.op_Implicit((Object)(object)drop.m_prefab))
						{
							continue;
						}
						string name = ((Object)drop.m_prefab).name;
						if (!hashSet3.Contains(name))
						{
							debug?.Invoke($"Unchanged {((Object)prefab).name} -> {name}: chance={drop.m_chance:G6}, amount={drop.m_amountMin}..{drop.m_amountMax}, levelMultiplier={drop.m_levelMultiplier}, onePerPlayer={drop.m_onePerPlayer}, dontScale={drop.m_dontScale}");
							continue;
						}
						num3++;
						if (!hashSet4.Add(name))
						{
							warn("Duplicate expected drop " + name + " on " + ((Object)prefab).name + ".");
						}
						Baseline value = originals.GetValue(drop, (Drop d) => new Baseline(d.m_chance));
						if (float.IsNaN(value.Chance) || float.IsInfinity(value.Chance))
						{
							warn("Invalid original chance for " + ((Object)prefab).name + " -> " + name + "; skipped.");
							continue;
						}
						float num5 = settings.Apply(value.Chance);
						if (drop.m_chance != num5)
						{
							num4++;
						}
						drop.m_chance = num5;
						debug?.Invoke($"{((Object)prefab).name} -> {name}: {value.Chance:G6} -> {num5:G6}");
						if (num5 == 0f)
						{
							warn(((Object)prefab).name + " -> " + name + ": base chance set to zero. Valheim 1.0.17 pseudo drops divide by chance without a zero guard; zero is not a verified disable switch.");
						}
					}
				}
				array2 = array;
				foreach (string text in array2)
				{
					if (!hashSet4.Contains(text))
					{
						warn("Expected drop " + text + " was not found on " + ((Object)prefab).name + ".");
					}
				}
				if (num3 != array.Length)
				{
					warn($"Expected {array.Length} armour mould entries on {((Object)prefab).name} but found {num3}.");
				}
				num += num3;
				num2 += num4;
				info($"{((Object)prefab).name}: {num3} target mould entries; {num4} entries modified.");
			}
			array2 = new string[2] { "JotunWarrior", "JotunWitch" };
			foreach (string text2 in array2)
			{
				if (!hashSet2.Contains(text2))
				{
					warn("Expected creature prefab " + text2 + " was not found.");
				}
			}
			info($"Found {num} armour mould drop entries (expected 9).");
			info($"Modified {num2} armour mould drop entries.");
			if (num != 9)
			{
				warn($"Expected 9 armour mould entries but found {num}. A Valheim update or another mod may have changed the drop tables.");
			}
		}
	}
	[BepInPlugin("com.weizhexu.armormoulddroprates", "ArmorMouldDropRates", "1.0.0")]
	public sealed class Plugin : BaseUnityPlugin
	{
		[HarmonyPatch(typeof(ZNetScene), "Awake")]
		private static class SceneAwakePatch
		{
			[HarmonyPostfix]
			[HarmonyPriority(0)]
			private static void Postfix(ZNetScene __instance)
			{
				if ((Object)(object)instance != (Object)null)
				{
					instance.Apply(__instance);
				}
			}
		}

		public const string Guid = "com.weizhexu.armormoulddroprates";

		private static Plugin instance;

		private Harmony harmony;

		private ChanceSettings settings;

		private bool debugLogging;

		private readonly DropProcessor processor = new DropProcessor();

		private readonly ConditionalWeakTable<ZNetScene, object> scenes = new ConditionalWeakTable<ZNetScene, object>();

		private void Awake()
		{
			//IL_00bb: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c5: Expected O, but got Unknown
			instance = this;
			ConfigEntry<string> val = ((BaseUnityPlugin)this).Config.Bind<string>("General", "Mode", "Multiplier", "Fixed or Multiplier (case-insensitive). Invalid values use Multiplier. Restart after editing.");
			ConfigEntry<float> val2 = ((BaseUnityPlugin)this).Config.Bind<float>("General", "FixedChance", 0.1f, "Used in Fixed mode. 0 = 0%, 0.5 = 50%, 1 = 100%. Clamped to [0,1].");
			ConfigEntry<float> val3 = ((BaseUnityPlugin)this).Config.Bind<float>("General", "Multiplier", 3f, "Used in Multiplier mode. 2 doubles the existing base mould chance, capped at 100%. Negative values clamp to zero.");
			debugLogging = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "DebugLogging", false, "Log target changes and unchanged entries with their metadata.").Value;
			settings = new ChanceSettings(val.Value, val2.Value, val3.Value, delegate(string s)
			{
				((BaseUnityPlugin)this).Logger.LogWarning((object)s);
			});
			harmony = new Harmony("com.weizhexu.armormoulddroprates");
			harmony.PatchAll(typeof(Plugin).Assembly);
			((BaseUnityPlugin)this).Logger.LogInfo((object)"Initialized.");
			((BaseUnityPlugin)this).Logger.LogInfo((object)$"Mode: {settings.Mode}, FixedChance: {settings.FixedChance}, Multiplier: {settings.Multiplier}");
		}

		private void Apply(ZNetScene scene)
		{
			if (!scenes.TryGetValue(scene, out var _))
			{
				List<GameObject> list = new List<GameObject>(scene.m_prefabs);
				list.AddRange(scene.m_nonNetViewPrefabs);
				processor.Process(list, settings, delegate(string s)
				{
					((BaseUnityPlugin)this).Logger.LogInfo((object)s);
				}, delegate(string s)
				{
					((BaseUnityPlugin)this).Logger.LogWarning((object)s);
				}, debugLogging ? ((Action<string>)delegate(string s)
				{
					((BaseUnityPlugin)this).Logger.LogInfo((object)s);
				}) : null);
				scenes.Add(scene, new object());
			}
		}

		private void OnDestroy()
		{
			Harmony obj = harmony;
			if (obj != null)
			{
				obj.UnpatchSelf();
			}
			if ((Object)(object)instance == (Object)(object)this)
			{
				instance = null;
			}
		}
	}
}